Základy JSON v Delphi

vložil Radek Červinka 19. října 2010 23:34

V tomto článku popíši základy použití JSON v Delphi.

Článek mi byl zaslán a autor chce zůstat jen pod nickem <z>.

Několik základních faktů o JSON

  • JSON je jednoduchý formát určený a vyvinutý přímo pro výměnu dat.
  • JSON je alternativa ke XML, vhodnost jednoho nebo druhého je individuální.
  • Úspora v zapsání stejných dat v JSON oproti XML může být až 40% (je to dáno zřejmě tím, že se nevyužívájí párové tagy).
  • JSON je velmi stabilní formát, tj. neexituje verze 1, 2, …
  • Data v JSON jsou ukládána pomocí Unicode (UTF-8), a proto odpadá problém při využití různých kódování.
  • Data v JSON mohou obsahovat jak páry (název a k tomu hodnota), tak i samotné hodnoty (pole hodnot).

malá ukázka struktury JSON:

{
  "name": "Henri Gourvest", /* this is a comment */
  "vip": true,
  "telephones": ["000000000", "111111111111"],
  "age": 33,
  "size": 1.83,
  "adresses": [
    {
      "adress": "blabla",
      "city": "Metz",
      "pc": 57000
    },
    {
      "adress": "blabla",
      "city": "Nantes",
      "pc": 44000
    }
  ]
}

Více o JSON a o struktuře lze nalézt na json.org (včetně možných datových typů), nebo v dříve odkazovaném článku co napsal o JSON Paweł Głowacki.

V Delphi je možné využít více knihoven k práci s JSON (viz. předchozí článek o JSON). Zde představím jen lkJSON a SuperObject.

Obě knihovny lze použít ve všemožných verzích Delphi i FPC, jsou volně ke stažení i se zdrojovým kódem.

lkJSON

V balíku lkJSON jsou dodány jednoduché příklady. Zde jsou komentované základy.

var
  js:TlkJSONobject;
  i:Integer;
begin
  // vytvoreni objektu
  //js := TlkJSONobject.Create; // jednoduche vytvoreni
  js:=TlkJSON.ParseText('{}') as TlkJSONobject; // vytvoreni parsovanim

  // kontrola
  if js=nil then
    Exit;

  try
    // pridavani polozek, nazev je string, hodnota může vzít různé typy
    js.Add('jmeno','hodnota-1');
    // pridat dalsi polozku se stejnym jmenem u lkJSON jde
    // hodnotu uz ale vzit asi nejde
    js.Add('jmeno','hodnota-2');

    // pridani polozky, ktera jako hodnotu obsahuje dalsi objekt {}
    js.Add('objekt',TlkJSONobject.Create);

    // pridani polozky do pod-objektu
    (js.Field['objekt'] as TlkJSONobject).Add('jmeno-objekt','hodnota-3');

    js.Add('pole',TlkJSONlist.Create); // vytvoreni pole []
    (js.Field['pole'] as TlkJSONlist).Add(20); // pridani polozky do pole

    // cteni polozek
    ShowMessage('Podle indexu: '+js.getString(0)); // podle indexu
          // pomoci .Field a indexu
    ShowMessage('Pomoci field: '+(js.FieldByIndex[1] as TlkJSONstring).Value); 
    ShowMessage('Podle nazvu: '+js.getString('jmeno')); // pomoci nazvu

    // cteni z podobjektu
    ShowMessage('Podobjekt: '+
           (js.Field['objekt'] as TlkJSONobject).getString('jmeno-objekt'));

    // prochazeni struktury JSON
    // asi lze vyuzit "for … in … do" jako u SuperObject, ale to jsem netestoval 
    for i := 0 to js.Count - 1 do
      case js.FieldByIndex[i].SelfType of
        jsString: ShowMessage('Prochazeni: '
             +VarToStr((js.FieldByIndex[i] as TlkJSONstring).Value));
        // …
      end;    

    // generovani JSON datove struktury pro prenos
    //Memo1.Lines.Text:=TlkJSON.GenerateText(js); // jen text
    i:=0;
    Memo1.Lines.Text:=GenerateReadableText(js,i); // strukturovany text
  finally
    //js.Free;
    FreeAndNil(js);
  end;
end;

SuperObject

Použití SuperObject je o něco jednodušší. Více informací lze zjistit zde: code.google.com/p/superobject/wiki/first_steps

var
  json: ISuperObject;

  // promenne pro prochazeni
  item1: ISuperObject;
  item2: TSuperAvlEntry;
  item3: TSuperObjectIter;
begin
  // vytvoreni
  json := SO(); // v zavorce lze parsovat JSON

  if json=nil then
    Exit; // jen pro jistotu, ze objekt byl vytvoren

  // jeste pro jistotu, ze to je objekt {}
  // pri parsovani prazdneho textu neni a nefungovalo by to spravně
  // jeste to muze byt pripadne pole []
  if not json.IsType(stObject) then
    begin
      json:=nil;
      Exit;
    end;

  try
    // pridavani a cteni polozek
    // S[] - string
    // I[] - integer
    // B[] - boolean …
    // specialni vyznam v nazvu maji tecka, (), [], mozna neco dalsiho
    // json.ForcePath('nazev-prvku'); //zaruci existenci prvku
    json.S['jmeno-str']:='hodnota-1';
    json.I['jmeno-int']:=11;

    // prace se strukturou, slozitejsi metoda
    json.O['objekt']:=SO; // vytvoreni objektu, stejne jako ForcePath
    json.O['objekt'].S['jmeno-sub']:='hodnota-2'; // pridani polozky

    // zkraceny zapis pro vytvareni i cteni
    // tecka znaci praci s hodnotou, ktera je v podobjektu 
    // hranate zavorky slouzi k praci s hodnotami pole
    // kulate zavorky jsou pro volani metod - zde je mozne se vyuzit hodnot v JSON
    // knihovna ohlida a vytvori potrebnou strukturu
    json.S['objekt-2.jmeno-sub-1']:='hodnota-3'; // vytvori objekt i polozku
    json.S['pole[]']:='polozka pole'; // vytvoreni a pridani polozky do pole
    json.S['pole[2]']:='polozka dalsi'; // vytvoreni a pridani polozky na pozici

    // cteni polozky podle indexu
    // (nevim o jinym jednodussim reseni)
    ShowMessage(json.AsObject.GetValues.AsArray[3].AsString);

    // prochazeni vsech polozek - moznost 1
    // POZN.: Array ma polozku Length, Object ma Count    
    for item1 in json do
      case item1.DataType of
        // superobject dovoluje cist integer jako string
        stString, stInt: ShowMessage('Prochazeni 1: '+item1.AsString);
        //…
      end;

    // prochazeni - moznost 2
    for item2 in json.AsObject do
    begin
      case item2.Value.DataType of
        stString, stInt: ShowMessage('Prochazeni 2: '+item2.Name+' : '
           +item2.Value.AsString);
        // …
      end;
    end;

    // prochazeni - moznost 3
    if ObjectFindFirst(json, item3) then
    repeat
      case item3.val.DataType of
        stString, stInt: ShowMessage('Prochazeni 3: '+item3.key+' : '+
           item3.val.AsString);
        // …
      end;
    until not ObjectFindNext(item3);
    ObjectFindClose(item3);

    // v zavorce - strukturovany / nestrukturovany vystup
    Memo1.Lines.Text:=json.AsJSon(True);
  finally
    // uvolneni
    json:=nil;
  end;
end;

Osobně využívám SuperObject. Výhodou je jednodušší přístup k jednotlivým prvkům, podobjektům, polím, kde není potřeba se o nic starat. SuperObject si take poradi s parsováním některých ne-validních sturktur a umožňuje parsovat i XML a převést do JSON. Malou vyhodou lkJSON je jednodussi vyber polozek pomoci indexu (.Field), na to se ale v JSON spolehat neda.

Rychlost práce obou knihoven je přibližně stejná. JSON používám namísto INI k ukládání dat. Pro srovnání, udělal jsem si test pro zápis tisíců hodnot a načtení x-té položky:

  • INI 3 sekundy!!
  • lkJSON 32 ms
  • SuperObject 16ms

Pokud potřebujete průběžně ukládat velké množství dat, JSON se určitě vyplatí využít. Je potřeba si dávat pozor při procházení podobjektů, jestli v dané struktuře existují.

Jestli někdo potřebuje nějakou pomoc, stačí napsat a další informace doplním.

<z>

Pozn.: Moc díky za článek. Pokud máte odvahu napsat podobný článek s tématikou Delphi, rád ho uveřejním a myslím, že to ocení všichni čtenáři Delphi.cz (a není jich málo).

Tagy: ,

Praxe

Komentáře

20.10.2010 6:55:13 #

pepak

SuperObject jsem dosud neviděl a nevím o něm nic, než co je uvedeno ve článku. A v tom mě zarazilo pár věcí:

- To pojmenovávání metod a properties je doufám jen špatná alternativa a ne jediný způsob. Snad každá učebnice programování se snaží odradit od používání zkratek tam, kde jde použít celé jméno, a jak velmi rychle pozná každý, kdo musel po někom jiném přebírat starší kód, je zatraceně dobré se tím řídit. A SuperObject k tomu přistupuje konstruktorem SO(), properties B, S, I atd.?

- Zajímalo by mě, jestli to oddělování objektů tečkou nebo odkazování prvků pole indexem v hranatých závorkách je volitelné. Důvod: tečka i hranatá závorka jsou povolenými znaky v názvech komponent objektů, tzn. je naprosto přijatelné mít například JSON objekt { "a.b[1]": "abcd" }. Že to není jen teorie ukazuje použití JSON na YouTube, kde se zrovna tečky v názvech properties objevují hodně často.

- Nejsem si úplně jistý tou vhodností JSONu pro ukládání konfiguračních dat. Přeci jenom u konfigurace se dá očekávat, že ji uživatel bude chtít měnit, což je u XML snadné (existují pro to nástroje, které to umožní dělat přehledně a existují nástroje pro ověření správnosti takového dokumentu a případný převod do jiného dokumentu), zatímco pro JSON to je dočasně (konverze, editace) nebo trvale (validace) vyloučeno. Rychlost proti INI je ovšem pěkná (stálo by za to srovnat ji ještě s XML a se zápisem do registru).

pepak

20.10.2010 9:58:06 #

Jaroslav Dakš

Zrovna jsem si včera SuperObject trochu zkoušel, takže jen pár poznámek:
- property s názvem O, S, I, D ... se běžně (v příkladech, nebo na fóru o SuperObject) používají, V tomto případě mi až tak moc nevadí protože si lze snadno zapamatovat, že jde o Object, String, Integer, Double...
- "konstruktor" SO je funkce globální v unitě superobject.pas a není potřeba ji používat, kdo chce může objekt vytvářet voláním konstruktoru: TSuperObject.Create. V názvech property a funkce SO vidím autorovu snahu o zestručnění, nebo zpřehlednění kódu. Chcete-li, aby byl váš kód košer podle učebnic, nebo obecných zvyklostí, můžete použít metody AsObject, AsString...
Vhodnost použití formátu JSON / XML / INI pro uložení dat asi bude otázkou osobní preference a konkrétního řešení a nemá cenu pouštět se do debat co je lepší.

Jaroslav Dakš

20.10.2010 11:47:29 #

<z>

ad 1. dotaz)
to pojmenovani SO, S, B, I ... je opravdu ne prilis stastne, nemuzu to ale uplne zavrhnout - je to kratke, vystizne, ale pravda je, ze by to mohlo byt celymi slovy. Ja si na to zvykl.

ad 2. dotaz)
jo, to je dobra otazka ... taky me zajimalo, jestli to jde ... Na zacatku jsem o tom nevedel, a kdyz jsem tam ukladal nazvy s teckou, tak jsem se pak nestacil divit, co to vytvorilo za strukturu. Bohuzel jsem ale doted nenasel zpusob, jak si s tim poradit :(

ad 3. dotaz)
s tim bych si dovolil nesouhlasit ... mate na mysli spis manualni editaci, ze?
samozrjme JSON je prinejmensim srovnatelne s XML,
sice to nevypada tak prehledne, jako XML, ale to jen do te doby, nez se to strukturuje - viz.
http://json.parser.online.fr/
a nastroje na editaci take urcite budou existovat, validator take existuje (viz. odkaz)
... takze v tomhle ohledu lze mezi XML a JSON udelat rovnitko

navic ... kdyz to vyuzivam jako INI, tak nepotrebuju, aby se v tom nekdo stoural, tak jsem zakazal editaci (zakodovanim)

========================

V clanku  jsem se nesnazil nekoho nabadat, aby hned misto INI zacal pouzivat JSON. Jen jsem rekl, ze to vyuzivam, protoze je to pro me dost vyhodne, protoze je to mnohem rychlejsi. Samozrejme zalezi, komu co vyhovuje ;)
Do zadne hadky ohledne toho jsem se opravdu poustet nechtel.

<z>

20.10.2010 16:15:44 #

tz

> INI 3 sekundy!

Docela by mě zajímalo jestli pro test byla použita třída IniFiles.TIniFile nebo IniFiles.TMemIniFile?

tz

20.10.2010 16:56:19 #

<z>

MemIni ;)

<z>

20.10.2010 21:55:28 #

pingback

Pingback from topsy.com

Twitter Trackbacks for
        
        Delphi.cz | Základy JSON v Delphi
        [delphi.cz]
        on Topsy.com

topsy.com

21.10.2010 18:06:35 #

<z>

2 pepak:
tak jsem prisel na to, jak pouzit v nazvu tecku, zavorky apod ... je to potreba pouzit takto
json.AsObject['a.b.c[]'].AsString

<z>

Komentování ukončeno

Naše nabídka

MVP
Ing. Radek Červinka - Embarcadero MVP
profil na linkedin, Twitter:@delphicz

Nabízím placené poradenství a konzultace v oblasti programování a vývoje SW.
Dále nabízíme i vývoj speciálního software na zakázku.

Neváhejte nás kontaktovat (i ohledně reklamy).

love Delphi

O Delphi.cz

Delphi je moderní RAD nástroj podporující tvorbu nativních aplikací pro platformu Win32, Win64, Mac OSX, Linux a na iPhone a Android.

Delphi.cz je nezávislý portál pro uživatele Delphi. Portál není koncipován pro úplné začátečníky, i když i ti se zde nebudou nudit, ale spíše na programátory, kteří již něco znají a chtějí své znalosti dále rozvíjet a sledovat novinky.

Poslední komentáře

Comment RSS

Dle měsíců