Utiliser JSON sous Free Pascal avec la librairie FCL-JSON

JSON est un format d'échange de données tout comme le XML. Il présente l'avantage d'être très léger comparé à ce dernier, ce qui fait qu'il est surtout présent dans les applications web.

Dans cet article, vous trouverez les bases nécessaires pour comprendre JSON et aussi pour apprendre à utiliser la bibliothèque FCL-JSON pour manipuler ce format d'échange de données sous Free Pascal.

Commentez

Article lu   fois.

L'auteur

Profil ProSite personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction

Cet article présente le format d'échange de données JSON et montre comment utiliser la librairie FCL-JSON pour parser et manipuler ce format sous Free Pascal. La librairie FCL-JSON fait en effet partie des librairies standard distribuées avec Free Pascal; vous n'aurez donc pas besoin d'une librairie externe pour compiler les codes fournis dans cet article.

Les codes et les exemples de cet article sont réalisés et testés avec la version 0.9.28.2 de Lazarus utilisant Free Pascal 2.2.4. Une connaissance du Pascal Objet est nécessaire pour la compréhension de cet article.

Consulter le site officiel de JSON pour plus d'information sur ce format :

II. Présentation de JSON

JSON (JavaScript Object Notation - Notation Objet issue de JavaScript) est un format léger d'échange de données. Tout comme XML, il est facile à manipuler mais requièrt moins de ressources pour l'écriture ou l'analyse des donnés. JSON est un format indépendant de tout langage de programmation et utilise des concepts qu'on retrouve dans la plupart de ces langages.

JSON est un format complètement textuel et se base sur deux structures principales :

  • Une collection : Une collection est composée de couples nom/valeur que nous pouvons identifier avec les champs d'un type record ou d'un Objet.
    Si nous considérons la variable suivante :
 
Sélectionnez
Var collection : record
  Nom : String ;
  Age : Integer ; 
End;
//puis ... 
Collection.nom:='darrylsite';
Collection.age := 10;
						

La représentation en collection pourrait être :

 
Sélectionnez
{"nom" : "darrylsite",  "age" : 10 }
  • Une liste de valeurs ordonnées : En pascal, cette liste pourrait être un tableau (Array), ou une liste chainée.

En JSON, ces structures prennent la forme suivante :

Un objet JSON est un ensemble non ordonné et constitué de couples nom/valeur contenus entre une paire d'accolades {} et séparés par des virgules. La nom est une chaîne de caractères et doit être suivie des deux points (:). La valeur est soit une chaîne de caractère (String), un nombre (Number), un objet (object), un tableau (Array), un booléen (true, false) ou null.

Une chaîne de caractères est une suite de caractères délimitée par des guillemets (" "). Un tableau est un ensemble ordonné de valeurs contenues dans une paire de crochets ([]).

Un exemple d'objet JSON :

 
Sélectionnez
{
   "langage " : "pascal",
   "annee_creation" :1969,
   "compilateurs" :[
                                  {"nom" : "MIDlet Pascal", "plateforme" : "Windows"},
                                  {"nom" : "Lazarus", "plateforme" : "Windows, linux, macOs"}
                   ]
}

Cet objet JSON peut être codé en Pascal de la manière suivante :

 
Sélectionnez
Type
         TCompilateur = record
                                       Nom : String ;
                                       plateforme : String
                                     End ;
 
         TLangage= record
                               nom  : String;    
                               annee_creation :  integer;
                               compilateurs : Array of compilateurs   ;
                             end;
 
var pascal : TLangage;

Pour plus de d'informations sur JSON vous pouvez consulter le lien suivant :

III. Utilisation de JSON avec la librairie FCL-JSON

III-A. Constituants de la librairie FCL-JSON

La librairie FCL-JSON que nous allons utiliser est celle distribuée avec la version 2.2.4 de Free Pascal et qu'on retrouve dans la version 0.9.28.2 de Lazarus.
Les principales unités de cette librairie sont les suivantes :

  • l'unité fpjson contient la définition des classes représentant les différents types supportés par JSON,
  • l'unité jsonparser, dans laquelle est définie un parseur permettant de parser des flux de données,
  • l'unité jsonconf contient la classe permettant de créer et d'utiliser des fichiers de configuration au format JSON.

Dans la suite, nous allons uniquement travailler avec les unités fpjson et jsonparser.

III-B. Les types de données JSON

Les différents types supportés par JSON sont définis dans l'unité fpjson comme élément du type énuméré TJSONtype.

 
Sélectionnez
TJSONtype = (jtUnknown, jtNumber, jtString, jtBoolean, jtNull, jtArray, jtObject);

Pour chaque type de données JSON, l'unité définit une classe qui modélise ce type de données. La hiérarchie des classes disponibles est la suivante :

Image non disponible

III-B-1. La classe TJSONData

La classe TJSONData est une classe abstraite et mère de toutes les autres classes représentant les types de données JSON. Les principales méthodes et propriétés définies dans cette classe sont résumées dans le tableau suivant:

Méthodes ou propriétés rôle
Class function JSONType: TJSONType Donne le type JSON de l'objet
Procedure Clear Efface toutes les données de l'objet
property Value: variant ; Donne la valeur de l'objet dépendant de la valeur de la fonction JSONType
Property AsString : TJSONStringType ; Convertit un type donné en TJSONStringType
property Count : Integer ; Donne le nombre d'éléments disponibles pour les types TJSONrray et TJSONObject
Property AsFloat : TJSONFloat ; Convertit une un type donné en TJSONFloat
Property AsInteger : Integer ; Convertit une un type donné en Integer
Property AsBoolean : Boolean ; Convertit une un type donné en Boolean
Property IsNull : Boolean ; Indique si l'objet est NULL
Property AsJSON : String ; Donne la représentation JSON de l'objet


Pour les conversions entre les types, l'exception EJSON est lancée quand celle-ci échoue.

III-B-2. La classe TJSONNumber

La classe TJSONNumber est utilisée pour représenter les données numériques JSON. Pour des contrôles plus accrus, sont définies les classes TJSONIntegerNumber et TJSONFloatNumber dérivant de TJSONNUmber qui est une classe abstraite.

La nouvelle méthode partagée par ces deux classes est :

 
Sélectionnez
class function NumberType : TJSONNumberType

Indiquant ainsi le type du nombre où TJSONNumberType est défini comme suit :

 
Sélectionnez
TJSONNumberType = (ntFloat,ntInteger)

Les constructeurs de ces classes sont :

  • Pour la classe TJSONIntegerNumber
 
Sélectionnez
Constructor Create(AValue : Integer);
  • Pour la classe TJSONFloatNumber
 
Sélectionnez
// TJSONFloat = Extended;
Constructor Create(AValue : TJSONFloat);  
		

III-B-3. Les classes TJSONNull, TJSONBoolean et TJSONString

Les classes TJSONNull, TJSONBoolean et TJSONString ne définissent pas de nouvelles méthodes par rapport à la classe TJSONData.

Leurs constructeurs sont définis comme suit :

  • Pour la classe TJSONNull
 
Sélectionnez
Constructor Create;
  • Pour la classe TJSONBoolean
 
Sélectionnez
Constructor Create(AValue : Boolean); 
		
  • Pour la classe TJSONString
 
Sélectionnez
// TJSONStringType = AnsiString;
Constructor Create(AValue : TJSONStringType);
		

III-B-4. La classe TJSONArray

La classe TJSONArray représente les tableaux utilisés en JSON. Les méthodes de cette classe permettent principalement de manipuler les éléments du tableau (ajouter, supprimer des éléments) et d'accéder à ces différents éléments.

Les constructeurs de cette classe sont :

 
Sélectionnez
Constructor Create;
    Constructor Create(const Elements : Array of Const);

La série des méthodes add(...) permettent d'ajouter des éléments dans le tableau et l'index de l'élément est retourné par celles-ci.

 
Sélectionnez
    function Add(Item : TJSONData): Integer;
    function Add(I : Integer): Integer;
    function Add(S : String): Integer;
    function Add: Integer;
    function Add(F : TJSONFloat): Integer;
    function Add(B : Boolean): Integer;
    function Add(AnArray : TJSONArray): Integer;
    function Add(AnObject: TJSONObject): Integer;
		

Il existe aussi les méthodes delete et remove qui permettent d'enlever un élément dans le tableau en connaissant soit l'index de ce dernier, soit l'objet à enlever. La méthode clear enlève tous les éléments du tableau.

 
Sélectionnez
Procedure Delete(Index : Integer);
    Procedure Remove(Item : TJSONData);
    Procedure Clear;
		

Pour explorer et utiliser les éléments du tableau, nous disposons des propriétés suivantes disponibles en lecture/écriture, permettant d'accéder à un élément dudit tableau en fonction de son type.

 
Sélectionnez
    Property Nulls[Index : Integer] : Boolean ;
    Property Integers[Index : Integer] : Integer ;
    Property Strings[Index : Integer] : TJSONStringType;
    Property Floats[Index : Integer] : TJSONFloat;
    Property Booleans[Index : Integer] : Boolean;
    Property Arrays[Index : Integer] : TJSONArray;
    Property Objects[Index : Integer] : TJSONObject;
        

Il nous aît bien sûr possible de connaitre le type d'un élément ou de rechercher si un objet donné se trouve dans le tableau avec les méthodes suivantes :

 
Sélectionnez
Property Types[Index : Integer] : TJSONType;
function IndexOf(obj: TJSONData): Integer;
         

La méthode IndexOf retourne -1 si aucun élément n'a été trouvé. L'indice du premier élément du tableau est 0.

III-B-5. La classe TJSONObject

La dernière classe TJSONObject contient les méthodes et attributs permettant d'utiliser les objets JSON. Vous noterez sans doute une ressemblance avec la classe TJSONArray. En effet, on peut voir les objets JSON comme des tableaux associatifs : on utilise des noms pour identifier les éléments au lieu d'indices numériques.

Les constructeurs de cette classe sont :

 
Sélectionnez
constructor Create;
    Constructor Create(const Elements : Array of Const); 
		

Les méthodes permettant la manipulation des données sont :

 
Sélectionnez
function Add(const AName: TJSONStringType; AValue: TJSONData): Integer;
    function Add(const AName: TJSONStringType; AValue: Boolean): Integer; 
    function Add(const AName: TJSONStringType; AValue: TJSONFloat): Integer;
    function Add(const AName: TJSONStringType; AValue: TJSONStringType): Integer;
    function Add(const AName: TJSONStringType; Avalue: Integer): Integer; 
    function Add(const AName: TJSONStringType): Integer;
    function Add(const AName: TJSONStringType; AValue : TJSONArray): Integer; 
    procedure Delete(Index : Integer);
    procedure Remove(Item : TJSONData);
    Procedure Clear;  override;

De même que pour les tableaux, la classe dispose des méthodes d'accès suivantes :

 
Sélectionnez
Property Types[AName : String] : TJSONType;
    Property Nulls[AName : String] : Boolean;
    Property Floats[AName : String] : TJSONFloat;
    Property Integers[AName : String] : Integer;
    Property Strings[AName : String] : TJSONStringType;
    Property Booleans[AName : String] : Boolean;
    Property Arrays[AName : String] : TJSONArray;
    Property Objects[AName : String] : TJSONObject;   

En plus de ces méthodes, la classe contient les méthodes regroupées dans le tableau suivant :

Méthodes ou propriétés Rôle
function IndexOf(Item: TJSONData): Integer; Donne l'indice d'un élément dans le tableau associatif
Function IndexOfName(const AName: TJSONStringType): Integer; Donne l'indice correspondant à une clé donnée
property Names[Index : Integer] : TJSONStringType Donne la clé correspondant à un indice donné
property Elements[AName: string] : TJSONData Donne un élément correspondant à une clé donnée


Il convient de s'attarder un peu sur le constructeur de cette classe prenant en paramètre un tableau variable. Examinons le code suivant :

 
Sélectionnez
Var monObjet  : TJSONObject;
Begin
  monObjet := TJSONObjet.create(['game', 'Killer Instinct', 'niveau' , 5, 'nitendo 64', true ]);
End;

Le nombre d'éléments du tableau est toujours en nombre pair représentant ainsi, le couple nom/valeur de la collection. L'objet précédemment crée correspond dans la notation JSON à

 
Sélectionnez
{
     "game" : "Killer Instinct",
      "Niveau" : 5,
      "Nitendo 64" : true
   }

IV. Parser des données JSON

Pour parser des flux de données, nous disposons de la classe TJSONParser définie dans l'unité jsonparser.

Pour utiliser des données JSON disponibles dans un flux, nous commencerons toujours par créer une instance du parseur pour ce flux. Selon que le flux est un fichier ou une chaîne de caractères, nous pouvons utiliser l'une des formes suivantes du constructeur Create :

 
Sélectionnez
    Constructor Create(Source : TStream); 
    Constructor Create(Source : TJSONStringType);  //TJSONStringType=AnsiString
 

Une fois l'objet créé, la méthode Parse permet d'avoir les données contenues dans le flux.

 
Sélectionnez
function Parse: TJSONData;

IV-A. Exemple de traitement d'un fichier contenant des données JSON

Considérons que nous ayons le fichier json.dat contenant les données suivantes :

json.dat
Sélectionnez
{
   "langage " : "pascal",
   "annee_creation" :1969,
   "compilateurs" :[
                                  {"nom" : "MIDlet Pascal", "plateforme" : "Windows"},
                                  {"nom" : "Lazarus", "plateforme" : "Windows, linux, macOs"}
                               ]
}

Nous pouvons parser le fichier avec le code suivant :

Parser un document json
Sélectionnez
program parsedemo;
{$mode objfpc}{$H+}
uses
  Classes, SysUtils, fpjson,jsonparser;
 
Procedure DoParse(Parseur : TJSONParser);
Var
  js : TJSONData;
begin
  Try
    js:=parseur.Parse;
    Try
      If Assigned(js) then
        begin
         Writeln(js.AsJSON)
        end
      else
        Writeln('Pas de données disponibles');
    Finally
      FreeAndNil(js);
    end;
  except
    On E : Exception do
      Writeln('Une erreur est survenue lors du traitement du fichier : ',E.Message);
  end;
end;
 
Procedure ParseFile (FileName : String);
Var
  flux : TFileStream;
  Parseur : TJSONParser;
begin
  flux:=TFileStream.Create(FileName, fmopenRead);
  try
    parseur:=TJSONParser.Create(flux);
    try
      DoParse(parseur);
    finally
      FreeAndNil(parseur);
    end;
  finally
    flux.Destroy;
  end;
end;
 
begin
  If FileExists('json.dat') then
    ParseFile('json.dat')
  else
    writeln('Le fichier est introuvable');
  readln;
end.

La procédure ParseFile crée un parseur pour le fichier json.dat, et appelle la procédure doParse qui parse le fichier et affiche les données JSON contenues dans celui-ci.

V. Quelques exemples

V-A. Parcours des données JSON

Le code qui suit permet de parser et afficher les données JSON contenues dans le fichier json.dat suivant :

json.dat
Sélectionnez
{
   "langage " : "pascal",
   "annee_creation" :1969,
   "compilateurs" :[
                                  {"nom" : "MIDlet Pascal", "plateforme" : "Windows"},
                                  {"nom" : "Lazarus", "plateforme" : "Windows, linux, macOs"}
                               ]
}

Le traitement de quelques exceptions n'a pas été fait afin d'alléger le code.

Parcours des données JSON
Sélectionnez
program parsedemo;
{$mode objfpc}{$H+}
uses
         Classes, SysUtils, fpjson,jsonparser;
 
procedure displayData(data : TJSONData);
 var lang, comp : TJSONObject;
     tab : TJSONArray;
     i : byte;
begin
  lang :=TJSONObject(data);
  writeln('Les caracteristique du langage : '+lang.Strings['langage']);
  writeln('Date de creation : ',lang.Integers['annee_creation']);
  writeln('------------- Les compilateurs disponibles ---------------- ');
  tab:=lang.Arrays['compilateurs'];
  for i:=0 to tab.Count-1 do
  begin
   comp:= tab.Objects[i];
   write('Nom : '+comp.Strings['nom']);
   write(' / ');
   writeln('Plateforme : '+comp.Strings['plateforme']);
  end;
  writeln('------------------------------------------ ---------------- ');
 
end;
 
Procedure DoParse(Parseur : TJSONParser);
Var
  js : TJSONData;
begin
  js:=parseur.Parse;
  displayData(js);
end;
 
Procedure ParseFile (FileName : String);
Var
  flux : TFileStream;
  Parseur : TJSONParser;
begin
  flux:=TFileStream.Create(FileName, fmopenRead);
  try
    parseur:=TJSONParser.Create(flux);
    try
      DoParse(parseur);
    finally
      FreeAndNil(parseur);
    end;
  finally
    flux.Destroy;
  end;
end;
 
begin
  ParseFile('json.dat')
  readln;
end.

Ce qui nous donne en sortie :

aperçu de la console
Sélectionnez
Les caracteristique du langage : pascal
Date de creation : 1969
------------- Les compilateurs disponibles ----------------
Nom : MIDlet Pascal / Plateforme : Windows
Nom : Lazarus / Plateforme : Windows, linux, macOs
---------------------------------------------------------------------

V-B. Création d'un objet JSON

Nous allons, dans cet exemple, créer par programme un document JSON dans le format ci-dessous représentant un petit carnet d'adresse :

carnet d'adresse au format JSON
Sélectionnez
{
 "personal" :
  [
   {"nom" : "Nabster", "email" :"nabster@darrylsite.tk"},
   {"nom" :"annis", "email" : "annis@darrylsite.tk"}
  ],
 "professional" :
  [
   {"nom" : "julius cesarius", "email" : "jcesarius@comesaf.com"},
   {"nom" : "nabistar", "email" : "nabistar@darrylsite.tk"}
  ]
}

J'ai écrit un bout code sous Lazarus avec une interface graphique permettant d'ajouter et supprimer des contacts.

Carnet d'adresse


Le code suivant est le code de l'unité contenant les données du programme :

Creation de données JSON
Sélectionnez
unit Unit2;
{$mode objfpc}{$H+}
interface
 
uses
     Classes, SysUtils, fpjson;
 
  Type
       ContactType =(personal, professional);
 
       TContact = class
 
                   public
                    constructor init();
                    procedure addContact(nom : String; email :String; tp : ContactType);
                    procedure removeContact(nom : String; email :String; tp : ContactType);
                    function getJSONData() : String;
                    destructor destroyed;
                    datas : TJSONObject;
                  end;
 
implementation
 
 constructor TContact.init();
 begin
  datas := TJSONObject.create();
  datas.add('personal', TJSONArray.Create);
  datas.add('professional', TJSONArray.Create);
 end;
 
 procedure TContact.addContact(nom : String; email : String; tp : ContactType);
  var cont : TJSONObject;
 begin
   cont := TJSONObject.Create(['nom', nom, 'email', email]);
  if (tp=personal) then
    datas.Arrays['personal'].Add(cont)
   else
    datas.Arrays['professional'].Add(cont);
 end;
 
 procedure TContact.removeContact(nom : String; email : String; tp : ContactType);
  var arr : TJSONArray;
      cont : TJSONObject;
      i : integer;
      trouve : boolean;
 begin
  if(tp=personal) then
    arr:=datas.Arrays['personal']
  else
    arr:=datas.Arrays['professional'];
  trouve := false;
  i:=0;
   while (not trouve) and (i<arr.Count) do
   begin
    cont := arr.Objects[i];
    if(cont.Strings['nom']=nom) and (cont.Strings['email']=email)then
     begin
      arr.Remove(cont);
      trouve:=true;
     end;
   end;
 end;
 
 function TContact.getJSONData() : AnsiString;
 begin
  result := datas.AsJSON;
 end;
 
 destructor TContact.destroyed;
 begin
  FreeAndNil(datas);
 end;
 
end.

VI. Conclusion et remerciements

VI-A. Conclusion

Nous avons donc exploré les bases du format d'échange de données JSON et de la librairie FCL-JSON. Si vous voulez plus de détails concernant l'utilisation de cette librairie, je vous recommande de consulter le code source de celle-ci, vu qu'il n'y a pas beaucoup de ressources disponibles sur le sujet sur le net.

VI-B. Remerciements

J'adresse tous mes remerciements à krachik et Alcatîz pour le temps qu'ils ont bien voulu passer à la correction et à l'amélioration de cet article.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2010 Kpizingui Darryl. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.