Utiliser JSON sous Free Pascal avec la librairie FCL-JSON

Darryl Kpizingui

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

- Titre : Utiliser JSON sous Free Pascal avec la librairie FCL-JSON

- Auteur : Darryl Kpizingui

- Parution : 23 mai 2010

- Licence : 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.

Sommaire

- I. Introduction

- II. Présentation de JSON

- III. Utilisation de JSON avec la librairie FCL-JSON

- III-A. Constituants de la librairie FCL-JSON

- III-B. Les types de données JSON

- III-B-1. La classe TJSONData

- III-B-2. La classe TJSONNumber

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

- III-B-4. La classe TJSONArray

- III-B-5. La classe TJSONObject

- IV. Parser des données JSON

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

- V. Quelques exemples

- V-A. Parcours des données JSON

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

- VI. Conclusion et remerciements

- VI-A. Conclusion

- VI-B. Remerciements

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 :

- le site officiel de JSON

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 requiert moins de ressources pour l'écriture ou l'analyse des données. 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 :

Var collection : record
Nom : String ;
Age : Integer ;
End;
//puis ...
Collection.nom:='darrylsite';
Collection.age := 10;

La représentation en collection pourrait être :

{"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. Le nom est une chaîne de caractères et doit être suivi des deux points (:). La valeur est soit une chaîne de caractères (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 :

{
"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 :

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 d'informations sur JSON vous pouvez consulter le lien suivant :

- Le site officiel de JSON

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éfini 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.

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 :

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 un type donné en TJSONFloat

Property AsInteger : Integer ;

Convertit un type donné en Integer

Property AsBoolean : Boolean ;

Convertit 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 :

class function NumberType : TJSONNumberType

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

TJSONNumberType = (ntFloat,ntInteger)

Les constructeurs de ces classes sont :

- pour la classe TJSONIntegerNumber

Constructor Create(AValue : Integer);

- pour la classe TJSONFloatNumber

// 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

Constructor Create;

- pour la classe TJSONBoolean

Constructor Create(AValue : Boolean);

- pour la classe TJSONString

// 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 :

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.

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.

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.

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 est 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 :

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 :

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

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

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 :

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 :

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éé correspond dans la notation JSON à :

{
"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 :

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.

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

{
"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

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

{
"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

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 caracteristiques 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

Les caracteristiques 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'adresses :

carnet d'adresses au format JSON

{
"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 de code sous Lazarus avec une interface graphique permettant d'ajouter et supprimer des contacts.

Télécharger le projet

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

Création de données JSON

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.