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.

    1var
    2  js:TlkJSONobject;
    3  i:Integer;
    4begin
    5  // vytvoreni objektu
    6  //js := TlkJSONobject.Create; // jednoduche vytvoreni
    7  js:=TlkJSON.ParseText('{}') as TlkJSONobject; // vytvoreni parsovanim
    8
    9  // kontrola
   10  if js=nil then
   11    Exit;
   12
   13  try
   14    // pridavani polozek, nazev je string, hodnota může vzít různé typy
   15    js.Add('jmeno','hodnota-1');
   16    // pridat dalsi polozku se stejnym jmenem u lkJSON jde
   17    // hodnotu uz ale vzit asi nejde
   18    js.Add('jmeno','hodnota-2');
   19
   20    // pridani polozky, ktera jako hodnotu obsahuje dalsi objekt {}
   21    js.Add('objekt',TlkJSONobject.Create);
   22
   23    // pridani polozky do pod-objektu
   24    (js.Field['objekt'] as TlkJSONobject).Add('jmeno-objekt','hodnota-3');
   25
   26    js.Add('pole',TlkJSONlist.Create); // vytvoreni pole []
   27    (js.Field['pole'] as TlkJSONlist).Add(20); // pridani polozky do pole
   28
   29    // cteni polozek
   30    ShowMessage('Podle indexu: '+js.getString(0)); // podle indexu
   31          // pomoci .Field a indexu
   32    ShowMessage('Pomoci field: '+(js.FieldByIndex[1] as TlkJSONstring).Value); 
   33    ShowMessage('Podle nazvu: '+js.getString('jmeno')); // pomoci nazvu
   34
   35    // cteni z podobjektu
   36    ShowMessage('Podobjekt: '+
   37           (js.Field['objekt'] as TlkJSONobject).getString('jmeno-objekt'));
   38
   39    // prochazeni struktury JSON
   40    // asi lze vyuzit "for … in … do" jako u SuperObject, ale to jsem netestoval 
   41    for i := 0 to js.Count - 1 do
   42      case js.FieldByIndex[i].SelfType of
   43        jsString: ShowMessage('Prochazeni: '
   44             +VarToStr((js.FieldByIndex[i] as TlkJSONstring).Value));
   45        // …
   46      end;    
   47
   48    // generovani JSON datove struktury pro prenos
   49    //Memo1.Lines.Text:=TlkJSON.GenerateText(js); // jen text
   50    i:=0;
   51    Memo1.Lines.Text:=GenerateReadableText(js,i); // strukturovany text
   52  finally
   53    //js.Free;
   54    FreeAndNil(js);
   55  end;
   56end;

SuperObject

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

    1var
    2  json: ISuperObject;
    3
    4  // promenne pro prochazeni
    5  item1: ISuperObject;
    6  item2: TSuperAvlEntry;
    7  item3: TSuperObjectIter;
    8begin
    9  // vytvoreni
   10  json := SO(); // v zavorce lze parsovat JSON
   11
   12  if json=nil then
   13    Exit; // jen pro jistotu, ze objekt byl vytvoren
   14
   15  // jeste pro jistotu, ze to je objekt {}
   16  // pri parsovani prazdneho textu neni a nefungovalo by to spravně
   17  // jeste to muze byt pripadne pole []
   18  if not json.IsType(stObject) then
   19    begin
   20      json:=nil;
   21      Exit;
   22    end;
   23
   24  try
   25    // pridavani a cteni polozek
   26    // S[] - string
   27    // I[] - integer
   28    // B[] - boolean …
   29    // specialni vyznam v nazvu maji tecka, (), [], mozna neco dalsiho
   30    // json.ForcePath('nazev-prvku'); //zaruci existenci prvku
   31    json.S['jmeno-str']:='hodnota-1';
   32    json.I['jmeno-int']:=11;
   33
   34    // prace se strukturou, slozitejsi metoda
   35    json.O['objekt']:=SO; // vytvoreni objektu, stejne jako ForcePath
   36    json.O['objekt'].S['jmeno-sub']:='hodnota-2'; // pridani polozky
   37
   38    // zkraceny zapis pro vytvareni i cteni
   39    // tecka znaci praci s hodnotou, ktera je v podobjektu 
   40    // hranate zavorky slouzi k praci s hodnotami pole
   41    // kulate zavorky jsou pro volani metod - zde je mozne se vyuzit hodnot v JSON
   42    // knihovna ohlida a vytvori potrebnou strukturu
   43    json.S['objekt-2.jmeno-sub-1']:='hodnota-3'; // vytvori objekt i polozku
   44    json.S['pole[]']:='polozka pole'; // vytvoreni a pridani polozky do pole
   45    json.S['pole[2]']:='polozka dalsi'; // vytvoreni a pridani polozky na pozici
   46
   47    // cteni polozky podle indexu
   48    // (nevim o jinym jednodussim reseni)
   49    ShowMessage(json.AsObject.GetValues.AsArray[3].AsString);
   50
   51    // prochazeni vsech polozek - moznost 1
   52    // POZN.: Array ma polozku Length, Object ma Count    
   53    for item1 in json do
   54      case item1.DataType of
   55        // superobject dovoluje cist integer jako string
   56        stString, stInt: ShowMessage('Prochazeni 1: '+item1.AsString);
   57        //…
   58      end;
   59
   60    // prochazeni - moznost 2
   61    for item2 in json.AsObject do
   62    begin
   63      case item2.Value.DataType of
   64        stString, stInt: ShowMessage('Prochazeni 2: '+item2.Name+' : '
   65           +item2.Value.AsString);
   66        // …
   67      end;
   68    end;
   69
   70    // prochazeni - moznost 3
   71    if ObjectFindFirst(json, item3) then
   72    repeat
   73      case item3.val.DataType of
   74        stString, stInt: ShowMessage('Prochazeni 3: '+item3.key+' : '+
   75           item3.val.AsString);
   76        // …
   77      end;
   78    until not ObjectFindNext(item3);
   79    ObjectFindClose(item3);
   80
   81    // v zavorce - strukturovany / nestrukturovany vystup
   82    Memo1.Lines.Text:=json.AsJSon(True);
   83  finally
   84    // uvolneni
   85    json:=nil;
   86  end;
   87end;

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).


Nabízíme Delphi školení a konzultace na různá témata, primárně ve Vaší firmě.

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

Partial English version.

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 nebo burzy práce).

Pokud chcete podpořit tento server libovolnou částkou, můžete použít PayPal. Moc děkuji.

Delphi Certified Developer

O Delphi.cz

Delphi je jediný moderní RAD nástroj podporující tvorbu nativních aplikací pro platformu Win32, Win64 , Mac OSX a na iPhone a Android (s výhledem na další platformy díky FireMonkey) na současném trhu (včetně Windows 8.1).

V současnosti je světová komunita přes dva miliónů vývojářů.

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.

Anketa

Poslední komentáře

Comment RSS