Fenner
Biblioteca

JSON e XML

Ler, navegar e escrever JSON sem exceções para o que falta, e o módulo de XML.

JSON#

O System.Json foi escrito para o caso que mais aperta: consumir a resposta de uma API de terceiro, que sempre manda o que não devia, como um campo ausente, um nulo onde se esperava número, um número onde se esperava texto, ou um objeto onde se esperava uma lista. Por isso a navegação não levanta exceção: Find, Item e Path devolvem nil, e os conversores têm valor padrão. Quem quiser rigor pede explicitamente (Member levanta; TryAs… informa).

json.fxp
program JsonDemo;

uses
  System.Json;

var
  Doc, Tag, Root: TJsonValue;
begin
  // navegação defensiva: Find, Item e Path devolvem nil, nunca levantam exceção
  Doc := TJsonValue.Parse(
    '{"id": 42, "name": "Ana", "price": 19.99, ' +
    '"tags": ["a", "b"], "address": {"city": "Recife"}}');
  try
    WriteLn('id:     ', Doc.Int64Of('id'));
    WriteLn('name:   ', Doc.TextOf('name'));
    // o número não passa por ponto flutuante: preço exato
    WriteLn('price*3: ', Doc.CurrencyOf('price') * 3);
    WriteLn('cidade: ', Doc.PathText('address/city'));
    WriteLn('cep:    ', Doc.PathText('address/zip', 'sem CEP'));

    Tag := Doc.Find('tags').First;
    while Tag <> nil do
    begin
      WriteLn('tag:    ', Tag.AsText);
      Tag := Tag.Next;
    end while;
  finally
    Doc.Dispose;
  end try;

  // construção: Put* num objeto, Add* numa lista
  Root := TJsonValue.NewObject;
  try
    Root.PutText('hello', 'mundo');
    Root.PutInt('n', 3);
    Root.PutArray('list').AddInt(1);
    WriteLn(Root.ToUString);
  finally
    Root.Dispose;
  end try;
end.
Saída
id:     42
name:   Ana
price*3: 59.9700
cidade: Recife
cep:    sem CEP
tag:    a
tag:    b
{"hello":"mundo","n":3,"list":[1]}

Números exatos#

O número não passa por ponto flutuante. O literal é guardado como veio e só é convertido quando alguém pede, em Int64 ou em Currency64 (ponto fixo). Um preço de marketplace vira dinheiro exato (19.99), não 19.989999….

Um mapa do TJsonValue#

GrupoMembros
LerTJsonValue.Parse(texto), TryParse(texto, valor, erro), ParseUtf8
FormaIsNull, IsBool, IsNumber, IsText, IsArray, IsObject, Count
Navegar (devolvem nil)Find(nome), Item(índice), Path('a/b/0/c'), First, Next
Atalhos por nomeTextOf, Int64Of, CurrencyOf, BoolOf (todos com padrão)
Atalhos por caminhoPathText, PathInt64, PathCurrency (atravessam o nil)
ConverterAsText, AsInt64, AsCurrency, AsBool (com padrão); TryAsInt64, TryAsCurrency
ConstruirNewObject, NewArray, Put… (em objeto) e Add… (em lista)
EscreverToUString, ToUtf8, WriteTo
RigorMember(nome) levanta EJsonMemberNotFound; CheckKind levanta EJsonKindMismatch

Path separa por barra: um segmento todo numérico indexa uma lista, e o resto procura um membro de objeto.

Memória#

O objeto nasce com +1 e a cadeia do pai retém outro +1. Quem recebe a raiz (de Parse ou de uma fábrica New…) é dono dela e a destrói com Dispose. O que Add…/Put… devolve é emprestado: pertence ao pai, não se solta. A profundidade máxima de aninhamento é limitada (200), para que uma entrada hostil não derrube a pilha.

XML#

O System.Xml traz um modelo de documento XML, com texto em UString. Está disponível nas duas RTLs.