System.JSON v Delphi XE6 a XE7, pokračování

vložil Igor Gottwald 1. října 2014 22:22

Pokud náhodou někdo hledá rozumnou dokumentaci k JSON implementaci v Delphi XE6 nebo XE7, tak spláče nad výdělkem. Nicméně knihovna doznala velkých rozšíření a rád bych jich pár představil:

Základní princip vychází z dědění vlastností od nejjednoduššího typu TJSONAncestor, který je maximálně obecný (a většina jeho metod je abstraktních), přes TJSONValue (základní jednotka, se kterou lze pracovat) dále pak konkrétní typy (TJSONNull, TJSONTrue, TJSONFalse, TJSONString a TJSONNumber) po implementaci objektů TJSONObject a polí TJSONArray.

TJSONAncestor obsahuje pro vývojáře především metody společné všem dalším objektům, kterými jsou:

  • funkce Null (tj. prázdný obsah reprezentovaný objektem TJSONNull)
  • příznak Owned (jen pro zápis, čte se pomocí GetOwned a určuje, zda se objekt uvolní společně s nadřazeným objektem, přičemž výchozí stav je True)
  • metoda Clone, která vytvoří kopii objektu s kompletní strukturou
  • vlastnost Value, která vrací textovou reprezentaci hodnoty (dle typu)
  • vlastnost EstimatedByteSize, která vrací odhadovaný počet bytů pro zápis, přičemž skutečný počet nebude větší
  • metoda ToBytes, která objekt zapíše do pole bytů maximální velikosti dle EstimatedByteSize

TJSONValue

rozšiřuje TJSONAncestor o generické metody TryGetValue<T> a GetValue<T>, přičemž se liší především tím, že GetValue vrací hodnotu nebo vyvolá výjimku, kdežto TryGetValue vrací hodnotu referencí a dále vrací příznak, zda byla hodnota vrácena. Obě metody mohou mít jako první parametr uvedenou cestu kombinací identifikátorů a indexů oddělených tečkou, např.

"id", "osoby.vedouci[1]", "[4]" apod.
  • textový identifikátor značí položku objektu ("id")
  • index v hranatých závorkách značí položku pole
  • pokud se odkazujete na prvek pole objektu, není nutné vkládat tečku
  • <code> (tj. "vedouci1" = "vedouci.1") </code>
  • hodnota se automaticky transformuje na odpovídající datový typ, pokud je to možné, tj. např. číslo lze vrátit jako string nebo "2" jako integer, "2014-09-30T12:00:00Z" jako TDateTime apod.
Příklad: Id := AJsonValue.GetValue<Integer>('[4].id');

TJSONTrue a TJSONFalse reprezentují logické hodnoty True a False. V JSON se zapisují jako "true" a "false" (bez uvozovek). Vytvoření logické hodnoty se provede pouhým voláním konstruktoru třídy.

TJSONNull

reprezentuje prázdnou hodnotu.

TJSONString

reprezentuje obecný řetězec znaků. Rozšiřuje TJSONValue o následující:

  • metoda třídy Hex(Digit) vrací '0' až 'F' pro hodnotu 0 až 15 (pro naše účely není podstatná)
  • konstruktor Create vyrobí prázdný řetězec (pozor, nelze jej dále měnit!)
  • konstruktor Create(Value: string) vyrobí JSON implementaci předané hodnoty
  • metoda AddChar(Ch: WideChar) přidá znak na konec aktuální hodnoty

Metoda ToString vrací textovou hodnotu v dvojitých uvozovkách, přičemž případné uvozovky uvnitř textu jsou zdvojeny. Metoda ToBytes zajistí korektní reprezentaci znaků Unicode.

TJSONNumber

reprezentuje obecné číslo. Rozšiřuje TJSONString o následující:

  • konstruktor Create(Double|Integer|Int64)
  • funkce ToString vrací nelokalizovanou reprezentaci čísla pro JSON
  • funkce Value vrací lokalizovanou textovou reprezentaci čísla
  • funkce AsInt, AsInt64 a AsDouble vrací odpovídající číselnou hodnotu

TJSONObject

reprezentuje objekt JSON, který obsahuje pojmenované libovolné hodnoty včetně objektů a polí. Jeho zápis v JSON je ve složených závorkách a vypadá zhruba takto:

{"id":1,"name":"John Black","children":["Marry","Peter"]} 

a rozšiřuje TJSONValue o následující:

  • přetížená metoda třídy ParseJSONValue, která analyzuje předaná data (řetězec, pole bytů) a vytvoří instanci TJSONValue (nebo vrátí nil, pokud někde nastala chyba)
  • konstruktor Create, který vytvoří prázdný objekt
  • konstruktor Create(Pair), který vytvoří objekt s jednou párovou hodnotu (viz. dále)
  • funkce GetValue(Name), který vrací první hodnotu TJSONValue odpovídající zadanému jménu
  • přetížená metoda AddPair, která přidá nebo vytvoří a přidá párovou značku a vrací instanci objektu, aby bylo možné volání řetězit
  • metoda RemovePair(Name) odstraní první nalezenou párovou hodnotu daného jména
  • metoda SetPairs nahradí párové značky předaným seznamem (uvolnění objektu seznamu z paměti je dále v režii TJSONObject) - pozor, nahrazené párové hodnoty nejsou uvolněny!
  • vlastnost Count vrací počet párů objektu
  • indexovaná vlastnost Pair vrací párovou hodnotu dle pořadí
  • vlastnost Values vrací hodnotu dle názvu (nebo nil, pokud neexistuje)

Párová hodnota je reprezentována objektem TJSONPair, který obsahuje hodnoty JsonString (textový název hodnoty) a JsonValue (objekt TJSONValue). Třídu TJSONObject lze použít v konstrukci for-in, přičemž řídící objekt je typu TJSONPair.

TJSONArray

reprezentuje pole libovolných hodnot, tj. včetně objektů a polí. Pole se v JSON zapisují hranatými závorkami, např.

[2,3,5,7,11,13,17,19]

a rozšiřuje TJSONValue o následující:

  • přetížený konstruktor Create s možností definovat až dva prvky TJSONValue nebo string
  • vlastnost Count vrací počet prvků pole
  • indexovaná vlastnost Item vrací prvek dle pořadového čísla
  • funkce Remove(Index) odebere zvolený prvek z pole a vrátí jej (tj. volající metoda jej musí uvolnit!)
  • metoda AddElement přidá do pole jeden prvek TJSONValue
  • přetížená metoda Add vytvoří objekty JSON a přidá je do pole z typů string, Integer, Double, Boolean nebo přidá TJSONArray a TJSONObject, přičemž vrací instanci na sebe sama, aby bylo možné volání řetězit
  • metoda SetElements nahradí všechny prvky polen předaným seznamem (uvolnění objektu seznamu z paměti je dále v režii TJSONArray) - pozor, nahrazené objekty nejsou uvolněny!

Hodnoty pole jsou vždy následovníci třídy TJSONValue.

Třídu TJSONArray lze použít v konstrukci for-in, přičemž řídící objekt je typu TJSONValue.

No a to je v zásadě vše, co je k použití jednotky System.JSON potřeba znát. Dále je nutné pamatovat na to, že se objekty JSON uvolňují přes objekt nejvyšší úrovně, pokud tedy nezměníte parametr Owned.

Igor Gottwald

Tagy: , ,

Novinky v Delphi

Komentáře

2.10.2014 23:54:20 #

Igor Gottwald

Ještě je nutné doplnit jistý celkem závažný problém v implementaci knihovny (přinejmenším v XE6 Update 1), který se projevuje při konverzi čísel s plovoucí řádkou v českém prostředí.
Naneštěstí metody TJSONValue.GetValue<Double> nebo TJSONValue.TryGetValue<Currency> apod. vůbec nepočítají s něčím, jako je jiný oddělovač desetinných čísel, a tak při konverzi textů typu "123.456" vznikne výjimka EConvertError: "'123.456' is not a valid floating point value"
Jedinou možnou opravou je přímá editace knihovny System.JSON.
Chybu jsem již nahlásil na Quality Central pod číslem 128034, kde je jak detailní popis, tak i opravený kód a ukázkový příklad.

Igor Gottwald

2.10.2014 23:55:15 #

Igor Gottwald

(samozřejmě ne plovoucí řádkou, ale plovoucí čárkou)

Igor Gottwald

3.10.2014 0:32:59 #

radekc

To máš asi pravdu, jelikož dle http://www.json.org/ je jediným možným desetinným oddělovačem .

radekc

3.10.2014 1:10:23 #

Igor Gottwald

Ještě poznámka k ukládání časových údajů do JSON. Na to je nejlepší použít funkci DateToISO8601, kterou naleznete v System.DateUtils. Tedy samozřejmě pokud si nepotřebujete pohrát s časovými zónami, letním časem apod. Implementace Systém.JSON neobsahuje žádné metody pro převod TDateTime na text, ale pro dekódování volá funkci ISO8601ToDate (a vrací jej jako UTC).

Igor Gottwald

3.10.2014 9:47:16 #

daks

Pokud náhodou někdo hledá rozumnou dokumentaci k JSON implementaci v Delphi XE6 nebo XE7, tak spláče nad výdělkem.
Tedy u komerčního produktu bych spíš očekával opak...

daks

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ů