Results 1 to 1 of 1

Thread: json artikel voor Lazarus

  1. #1
    John Kuiper
    Join Date
    Apr 2007
    Location
    Almere
    Posts
    8,747

    json artikel voor Lazarus

    JSON is een populair dataformaat om gegevens snel over te brengen tussen verschillende systemen.
    De data is puur tekst en voor streaming heel interessant.
    Nu zijn er veel voorbeelden om JSON data te lezen en te parsen (onderverdelen in verschillende items).
    Zo kan arrays en objecten in JSONdata staan met een hoeveelheid informatie.
    Maar hoe wordt JSONdata gemaakt.
    In deze voorbeelden wordt uitgegaan van Lazarus / FPC waarvan fcl-json een onderdeel is.
    Om JSONdata te kunnen gebruiken heb je ieder geval de units fpjson en jsonparser nodig.
    In de meeste gevallen wordt als eerste een object gemaakt.
    Deze wordt altijd afgesloten met ‘{}’.
    In het object kan een array gemaakt worden die ook meerdere objecten kan bevatten.
    Een array wordt altijd afgesloten met ‘[]’.

    Een voorbeeld:
    Delphi Code:
    1. var MyObj1, MyObj2, RootObj  : TJSONObject;
    2.     MyArray         : TJSONArray;
    3.     TS              : TStringstream;
    4. begin
    5.  RootObj := TJSONObject.Create;
    6.  MyArray := TJSONArray.Create;
    7.  MyObj1   := TJSONObject.Create;
    8.  MyObj1.Add('name', 'john');
    9.  MyObj1.Add('age', 50);
    10.  MyArray.Add(MyObj1);
    11.  MyObj2   := TJSONObject.Create;
    12.  MyObj2.Add('name', 'pieter');
    13.  MyObj2.Add('age', 40);
    14.  MyArray.Add(MyObj2);
    15.  RootObj.Add('date',formatdatetime('yyyy-mm-dd',now));
    16.  RootObj.Add('mydatabase',MyArray);
    17.  
    18.  TS      := TStringstream.Create(RootObj.AsJSON);
    19.  memo1.lines.add(TS.DataString);
    20.  MyObj1.free;
    21.  MyObj2.free;
    22.  //RootObj.free;
    23. end;
    Hier maak ik drie objecten aan: een rootobject en twee objecten voor de array.
    In het rootobject maak ik twee entries aan, waarvan de eerste een datum is en de tweede de array met twee objecten.
    Het resultaat ziet er zo uit:
    Code:
    { "date" : "2020-05-04", "mydatabase" : [{ "name" : "john", "age" : 50 }, { "name" : "pieter", "age" : 40 }] }
    Deze code kan een stuk efficiënter worden door gebruik te maken van een interne functie, waardoor je dynamisch zoveel JSONobjecten kan maken als het nodig is:
    Delphi Code:
    1. procedure TForm1.BtnCreateClick(Sender: TObject);
    2. var RootObj  : TJSONObject;
    3.     MyArray         : TJSONArray;
    4.     TS              : TStringstream;
    5.  
    6. function ObjToArray(aName : string; aAge : integer) : TJSONObject;
    7. begin
    8.  result := TJSONObject.Create;
    9.  result.Add('name', aName);
    10.  result.Add('age', aAge);
    11. end;
    12.  
    13. begin
    14.  RootObj := TJSONObject.Create;
    15.  MyArray := TJSONArray.Create;
    16.  MyArray.Add(ObjToArray('john',50));
    17.  MyArray.Add(ObjToArray('pieter',40));
    18.  RootObj.Add('date',formatdatetime('yyyy-mm-dd',now));
    19.  RootObj.Add('mydatabase',MyArray);
    20.  
    21.  TS      := TStringstream.Create(RootObj.AsJSON);
    22.  memo1.lines.add(TS.DataString);
    23. end;

    Maar hoe zit het nu met het voorbeeld op deze site waarin wordt uitgelegd hoe je op een simpele manier een array van objecten kan inlezen, zoals met deze structuur.
    Voor het tonen van records is dit de ideale JSON model (naar mijn mening).
    De data begint met een ‘[‘ waardoor je aangeeft dat je in een array zit.
    Verder is voor elke record een object gemaakt.
    Daarna wordt de data afgesloten met een ‘]’.
    Normaal kan je met TJSONObject deze hele dataset opslaan als JSON.
    Echter kan TJSONArray geen data exporteren.
    Maar je kan wel typecasten.
    Delphi Code:
    1. procedure TForm1.BtnCreateClick(Sender: TObject);
    2. var  MyArray         : TJSONArray;
    3.       TS              : TStringstream;
    4.  
    5. function ObjToArray(aName : string; aAge : integer) : TJSONObject;
    6. begin
    7.  result := TJSONObject.Create;
    8.  result.Add('name', aName);
    9.  result.Add('age', aAge);
    10. end;
    11.  
    12. begin
    13. MyArray := TJSONArray.Create;
    14.  MyArray.Add(ObjToArray('john',50));
    15.  MyArray.Add(ObjToArray('peter',40));
    16.  TS      := TStringstream.Create(MyArray.AsJSON);
    17.  memo1.lines.add(TS.DataString);
    18. MyArray.free;
    19. end;
    Dit is het resultaat, zoals de website hierboven ook heeft:
    Code:
    [{ "name" : "john", "age" : 50 }, { "name" : "peter", "age" : 40 }]
    Nu komen we ergens.
    Met deze basis kan ik ook mijn gegevens uitbreiden.
    Ik maak wat meer objecten aan en voeg nog een extra array aan een object toe:
    Delphi Code:
    1. var MyArray         : TJSONArray;
    2.     TS              : TStringstream;
    3.  
    4. function ObjToArray(aName : string; aAge : integer; const aGames : array of const) : TJSONObject;
    5. begin
    6.  result := TJSONObject.Create;
    7.  result.Add('name', aName);
    8.  result.Add('age', aAge);
    9.  result.Add('games', TJSONArray.Create(aGames));
    10. end;
    11.  
    12. begin
    13.  MyArray := TJSONArray.Create;
    14.  MyArray.Add(ObjToArray('john',50,['boardgames','cardgames']));
    15.  MyArray.Add(ObjToArray('peter',40,['boardgames','cardgames','computergames']));
    16.  TS      := TStringstream.Create(MyArray.AsJSON);
    17.  fJSONResult := TS.DataString;
    18.  memo1.lines.add(fJSONResult);
    19.  MyArray.free;
    20. end;
    Als resultaat deze (geformatteerde) data:
    Code:
    [{
    	"name": "john",
    	"age": 50,
    	"games": ["boardgames", "cardgames"]
    }, {
    	"name": "peter",
    	"age": 40,
    	"games": ["boardgames", "cardgames", "computergames"]
    }]
    Het zal ook mooi zijn om de gemaakt JSONdata ook weer terug te lezen naar waarde, die je nodig hebt voor bijvoorbeeld een dataset of een objectlist.
    Beter goed afkijken dan weken uitzoeken, wat misschien niet tot het resultaat komt die je verwacht.
    Het wiel hoeft maar één keer worden uitgevonden.
    Op de weblog pagina van Marcus Fernström (https://medium.com/@marcusfernstrm/f...n-337c04cad489) staat een stukje code, waarmee je op een simpele manier de hele JSONarray kan uitlezen.
    Deze heb ik aangepast op de JSONdata, die ik heb gegeneerd van de bovenstaande code:
    Delphi Code:
    1. var people     : TJSONArray;
    2.     person     : TJSONObject;
    3.     personEnum : TJSONEnum;
    4. begin
    5.   people := TJSONArray(GetJSON(fJSONResult));
    6.   for personEnum in people do
    7.   begin
    8.     // Cast the enum value to person
    9.     person := TJSONObject(personEnum.Value);    // Output a few pieces of data as example.
    10.     memo1.lines.add(person.FindPath('name').AsString);
    11.     memo1.lines.add(person.FindPath('age').AsString);
    12.   end;
    13. end;
    Waar vooral hier handig gebruik is gemaakt, is de TJSONEnumerator.
    Dit record wordt tijdens het inlezen van de data al gevuld met gegevens, waardoor je heel snel door objecten kan lezen.
    Maar het is geen verplichting om enum te gebruiken.
    De traditionele manier met index werkt ook:
    Delphi Code:
    1. var people     : TJSONArray;
    2.     person     : TJSONObject;
    3.     index      : integer;
    4. begin
    5.   people := TJSONArray(GetJSON(fJSONResult));
    6.   for index := 0 to people.Count - 1 do
    7.   begin
    8.     person := people.Objects[index];
    9.     memo1.lines.add(person.FindPath('name').AsString);
    10.     memo1.lines.add(person.FindPath('age').AsString);
    11.   end;
    12. end;
    Her resultaat voor beide opties is dan:
    Code:
    john
    50
    peter
    40
    Maar ik heb in mijn JSONdata ook een extra array staan als een soort verzameling.
    Die wil ik ook graag kunnen zien.
    In de bovenstaande code maak ik een extra TJSONArray object aan.
    Daarin zet ik alle gegevens van de gevonden item (in dit geval ‘games’) in een aparte array.
    Eerst voer ik een controle uit of het gevonden item wel een array is.
    Zo ja, zet ik deze gegevens in mijn nieuwe JSONarray.
    Met een ‘for next’ loop lees deze gegevens d.m.v. tStrings 1 voor 1 en toont deze op het scherm.
    Delphi Code:
    1. var people     : TJSONArray;
    2.     games      : TJSONArray;
    3.     person     : TJSONObject;
    4.     personEnum : TJSONEnum;
    5.     index      : integer;
    6. begin
    7.   people := TJSONArray(GetJSON(fJSONResult));  
    8.   for personEnum in people do
    9.   begin
    10.     person := TJSONObject(personEnum.Value);    
    11.     memo1.lines.add(person.FindPath('name').AsString);
    12.     memo1.lines.add(person.FindPath('age').AsString);
    13.     memo1.lines.add('kind of games:');
    14.     if person.FindPath('games').JSONType = jtArray then
    15.     begin
    16.       games := TJSONArray(GetJSON(person.FindPath('games').AsJSON));
    17.       for index := 0 to games.Count - 1 do
    18.         memo1.lines.Add(games.Strings[index]);
    19.     end;
    20.     memo1.lines.add('');
    21.   end;
    22. end;
    Dit is het resultaat:
    Code:
    john
    50
    kind of games:
    boardgames
    cardgames
    
    peter
    40
    kind of games:
    boardgames
    cardgames
    computergames
    In het begin heb ik gebruik gemaakt van een object met daarin een array met meerdere objecten.
    Nu de array uitgelezen kan worden, kunnen we ook het (root)object gaan uitlezen op de volgende manier:
    Delphi Code:
    1. var rootObject : TJSONObject;
    2.     people     : TJSONArray;
    3.     person     : TJSONObject;
    4.     personEnum : TJSONEnum;
    5. begin
    6.   rootObject := TJSONObject(GetJSON(fJSONResult));
    7.   memo1.lines.add(rootObject.FindPath('date').AsString);
    8.   if rootObject.FindPath('mydatabase').JSONType = jtArray then
    9.   begin
    10.     people := TJSONArray(GetJSON(rootObject.FindPath('mydatabase').AsJSON));
    11.     for personEnum in people do
    12.     begin
    13.       person := TJSONObject(personEnum.Value);
    14.       memo1.lines.add(person.FindPath('name').AsString);
    15.       memo1.lines.add(person.FindPath('age').AsString);
    16.     end;
    17.   end;
    18.  rootObject.Free;
    19. end;
    Met lazarus kan je nog op een andere manier JSONdata inlezen.
    Met TJsonconfig (unit jsonconf) kan je op een simpele manier JSONdata via een bestand inlezen en opslaan.
    Er wordt gebruik gamaakt van TJSONobjecten.
    Eén object wordt aangemaakt en voor elke record een eigen object.
    Om een goed voorbeeld te maken, maak ik een recordstructuur aan met ‘name’ en ‘age’.
    Die koppel is aan een dynamische array en vul deze in de Formcreate van mijn Form.
    Met dit stukje code worden de records opgeslagen:
    Delphi Code:
    1. procedure TForm1.BtnConfSaveClick(Sender: TObject);
    2. var c: TJSONConfig;
    3.     index : integer;
    4.  
    5. procedure SetRecord(const aFieldnr : integer; aProperty, aValue : string);
    6. begin
    7.  c.SetValue(format('/record%d/%s',[aFieldnr + 1, aProperty]), aValue);
    8. end;
    9.  
    10. begin
    11.   c:= TJSONConfig.Create(Nil);
    12.   try
    13.     c.Filename:= 'config.json';
    14.     for index := 0 to length(fusers) - 1 do
    15.     begin
    16.       SetRecord(index,'name',fusers[index].name);
    17.       SetRecord(index,'age',fusers[index].age.ToString);
    18.     end;
    19.     memo1.lines.add(booltostr(c.Modified,true));
    20.   finally
    21.     c.Free;
    22.   end;
    23. end;
    Kleine uitleg.
    SetValue() wordt aangeroepen met twee parameters.
    De eerste parameter is in twee delen: het eerste deel geeft de property aan van het rootobject.
    Het tweede deel is voor het nieuwe object, die gekoppeld wordt aan de property.
    Dit wordt gescheiden met een slash (‘/’).
    Als de eerste property al bestaat, wordt het tweede deel toegevoegd aan het bestaande object.
    De laatste parameter is de waarde van property.
    Dit kan zijn: string, integer, float of een boolean.
    Het resulttaat wordt achter elkaar geplaatst.
    Hieronder heb ik even een format uitgevoerd hoe de JSONdata wordt opgemaakt:
    Code:
    { 
    "record1" : { "name" : "john", "age" : "50" }, 
    "record2" : { "name" : "peter", "age" : "40" }, 
    "record3" : { "name" : "mary", "age" : "44" }, 
    "record4" : { "name" : "jef", "age" : "23" } 
    }
    Dit stukje code laat de JSONdata vanuit een bestand en zet deze in de dynamische array:
    Delphi Code:
    1. procedure TForm1.btnConfCreateClick(Sender: TObject);
    2. var c       : TJSONConfig;
    3.     index   : integer;
    4.     subkeys : TStrings;
    5. begin
    6.   c:= TJSONConfig.Create(Nil);
    7.   subkeys := TStringlist.Create;
    8.   try
    9.     c.Filename:= 'config.json';
    10.     index := 1;
    11.     repeat
    12.       subkeys.Clear;
    13.       c.EnumValues(format('record%d',[index]),subkeys);
    14.       memo1.lines.add(format('aantal subkeys : %d',[subkeys.Count] ));
    15.       if subkeys.Count > 0 then
    16.       begin
    17.         setlength(fUsers,index);
    18.         fUsers[index - 1].name := c.GetValue(format('record%d/name',[index]), '');
    19.         fUsers[index - 1].age  := strtoint(c.GetValue(format('record%d/age',[index]), '0'));
    20.       end;
    21.       index := index + 1;
    22.     until subkeys.Count = 0;
    23.   finally
    24.     c.Free;
    25.     subkeys.free
    26.   end;
    27. end;
    Kleine uitleg.
    Functie Getvalue haalt de waarden uit de object, die gekoppeld is aan de property ‘record(nummer) en zet deze in de aangegeven property van de array.
    Als de waarde niet is gevonden, treedt de defaultwaarde (tweede parameter van de functie) op.
    Maar hoe weet je dat je aan het einde bent van je data?
    De property EnumValues kijkt of er properties aanwezig in de gekoppelde object en zet deze in een TStrings.
    Als in TStrings het aantel regels nul is, is het einde van de JSONdata inzicht.
    Opmerking: Ik had verwacht dat TStrings telkens op nul werd gezet, maar de gevonden properties worden toegevoegd aan de bestaande TStrings.
    Vandaar dat ik bij elke leesopdracht een TStrings.clear uitvoer.

    Dit is het wel zo’n klein beetje.
    Er kan veel meer inzitten om het juiste resultaat te krijgen.
    Maar dit onderstaande verhaal geeft een duidelijker beeld hoe je op een simpele manier toch met JSON kan werken.

    Met dank aan Bart(lazarus / nldelphi forum) en de fcl-json wiki voor het jsonfiggedeelte.

    Succes.
    Attached Files Attached Files
    Last edited by GolezTrol; 07-May-20 at 15:38.
    Delphi is great. Lazarus is more powerfull

Thread Information

Users Browsing this Thread

There are currently 2 users browsing this thread. (0 members and 2 guests)

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •