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 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 :
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 :
{
"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 :
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 :
{
"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.
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 :
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 :
{
"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.
Le code suivant est le code de l'unité contenant les données du programme :
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.