Um modelo de objeto JSON rápido e com economia de memória, com suporte para analisar e escrever com eficiência em formato compatível com JSON.
Esta biblioteca depende apenas do repositório NESLIB. É incluído como submódulo com este repositório.
O principal ponto de entrada para esta biblioteca é a interface IJsonDocument . É usado para analisar, carregar e salvar documentos JSON e fornece acesso ao modelo de objeto JSON. Você pode analisar uma corda JSON da seguinte maneira:
var
Doc: IJsonDocument;
begin
Doc := TJsonDocument.Parse( ' { "Answer" : 42 } ' );
end ;Observe que, diferentemente da especificação JSON oficial, esta biblioteca não requer citações em torno das teclas de dicionário (desde que a chave não contenha espaços ou outros caracteres não identificadores). Portanto, o seguinte também é válido:
Doc := TJsonDocument.Parse( ' { Answer : 42 } ' ); Você também pode usar o método Load para carregar de um arquivo ou fluxo.
No lado da saída, você usa Save para salvar em um arquivo ou fluxo, ou ToJson para produzir para uma string json.
Você também pode criar novos documentos JSON a partir do zero usando os métodos CreateArray ou CreateDictionary :
var
Doc: IJsonDocument;
begin
Doc := TJsonDocument.CreateArray;
Doc.Root.Add( 42 );
end ;Como você pode ver neste exemplo, você acessa o modelo de objeto de documento JSON através da propriedade raiz.
No coração do modelo de objeto JSON está o tipo TJsonValue . Este é um registro que pode conter qualquer tipo de valor JSON.
Ele fornece vários operadores de conversão implícitos para converter um TJsonValue em outro tipo (Delphi). Além disso, existem vários métodos To* que tentam converter um TJsonValue , mas retornam um valor padrão fornecido se a conversão falhar.
Você (nunca pode) criar TJsonValue mesmo; A única maneira de criar um TJsonValue é adicionando um valor ao JSON Array ou Dictionary:
var
Doc: IJsonDocument;
begin
Doc := TJsonDocument.CreateArray;
Doc.Root.Add( 42 );
end ; Este exemplo adiciona um TJsonValue (com valor 42) a uma matriz JSON. Para criar uma nova variedade de dicionários, você usa os métodos AddArray ou AddDictionary :
var
Doc: IJsonDocument;
Dict: TJsonValue;
begin
Doc := TJsonDocument.CreateArray;
Dict := Doc.Root.AddDictionary;
Dict.AddOrSetValue( ' answer ' , 42 );
end ;Isso cria um novo dicionário e o adiciona à matriz raiz. Em seguida, o valor 42 é adicionado a este dicionário sob o nome 'Resposta'.
Para verificar o tipo de valor, use a propriedade TJsonValue.ValueType ou um dos métodos TJsonValue.Is* .
Ao tentar usar métodos como Add (ou AddOrSetValue ) em valores que não são matrizes (ou dicionários), será levantada uma exceção.
No entanto, o acesso aos itens em uma matriz (usando a propriedade Items ) ou os valores em um dicionário (usando a propriedade Values ) nunca resultará em uma exceção, mesmo que o índice de matriz esteja fora dos limites. Isso permite encadear acessos múltiplos/dicionários juntos sem precisar verificar a validade de cada etapa intermediária. Por exemplo:
I := Doc.Root.Items[ 3 ].Values[ ' foo ' ].Values[ ' bar ' ].Items[ 4 ].ToInteger( 0 );Isso sempre terá sucesso, mas retorne 0 se algum dos valores intermediários não estiver disponível.
A interface IJsonDocument facilita a leitura e a gravação de JSON em um modelo de objeto de documento.
No entanto, você também pode optar por ler ou escrever JSON manualmente, se preferir (por exemplo, para evitar a necessidade de carregar um modelo de objeto na memória). Você pode fazer isso com as interfaces IJsonReader e IJsonWriter na unidade Neslib.Json.IO .
Essas interfaces são completamente independentes de qualquer implementação DOM e nem exigem a unidade Neslib.Json . O uso dessas interfaces é um pouco mais complicado e requer mais trabalho. Consulte a unidade Neslib.Json.IO para obter mais informações.
Há também uma implementação de JSONPATH do tipo XPath que você pode usar para consultar documentos JSON.
Não existe uma especificação oficial do JSONPATH, mas a versão mais usada parece ser desenvolvida por Stefan Goessner.
Um jsonpath parece:
$.store.book[0].titleou
$['store']['book'][0]['title'] Ambas as representação são idênticas: você pode usar a notação de ponto ( . ) Ou suporte ( [] ) para denotar filhos de um dicionário. Os colchetes também podem ser usados com índices numéricos para denotar filhos de uma matriz por índice.
O JSONPATH usa apenas citações únicas (') entre colchetes. Também permitimos citações duplas ("), pois elas são mais fáceis de usar nas cordas Delphi.
Resumidamente:
$ indicando a raiz, seguido por zero ou mais operadores crianças ( . Ou [] ). A $ por si só corresponde ao documento inteiro.* ou '*' ) para combinar com todas as crianças. Por exemplo, $.store.book[*].author corresponde aos autores de todos os livros da loja.. ), Um ponto duplo ( .. ) pode ser usado para procurar descendentes em vez de crianças imediatas. Por exemplo, $..author corresponde a todos os autores, independentemente da profundidade. Isso é chamado de ascendência recursiva.$.store.book[0,2,3] corresponde ao primeiro, terceiro e quarto livros.[Start:End:Step] Para combinar com uma fatia (alcance) de crianças. Isso corresponde a todas as crianças da Start do índice até (mas não incluindo) End , usando um determinado tamanho Step (geralmente 1). Todos são opcionais, mas pelo menos um valor (e cólon) deve ser dado:Start for omitido, está implícito em 0. Um valor negativo indica um deslocamento do final da matriz.End for omitida, a fatia extrata até o final da matriz. Um valor negativo indica e compensa a partir do final da matriz.Step for omitida, está implícito em 1.List[2:] corresponde ao terceiro e a todos os seguintes elementos.List[-2:] corresponde aos dois últimos elementos.List[:2] corresponde aos dois primeiros elementos.List[:-2] corresponde a todos, exceto os dois últimos elementos.List[2:-2] corresponde a todos os elementos, exceto os dois e os dois primeiros.List[-4:-2] corresponde aos 3º e 4º elementos do final.List[::2] corresponde a todos os elementos com um índice uniforme.O JSONPATH também possui um operador @ para permitir expressões de script personalizadas. Não apoiamos esse operador.
Exemplo de documento:
{ "store" : {
"book" : [
{ "category" : " reference " ,
"author" : " Nigel Rees " ,
"title" : " Sayings of the Century " ,
"price" : 8.95
},
{ "category" : " fiction " ,
"author" : " J. R. R. Tolkien " ,
"title" : " The Lord of the Rings " ,
"isbn" : " 0-395-19395-8 " ,
"price" : 22.99
}
],
"bicycle" : {
"color" : " red " ,
"price" : 19.95
}
}
}Exemplo de caminhos:
| Expressão | Resultado |
|---|---|
$ | Corresponde ao documento raiz (um único valor) |
$..* | Corresponde a todos os membros do documento (muitos valores) |
$.store.book[*].author | Os autores de todos os livros da loja |
$..author | Todos os autores |
$.store.* | Todas as coisas na loja (2 livros e uma bicicleta) |
$.store..price | O preço de tudo na loja |
$..book[2] | O terceiro livro |
$..book[-1:] | O último livro em ordem |
$..book[:2] | Os dois primeiros livros |
A API do JSONPATH é curta e simples. Consiste em um registro TJsonPath com apenas alguns métodos.
Para correspondência única, use o método Match estática:
var
Doc: IJsonDocument;
Matches: TArray<TJsonValue>;
begin
Doc := TJsonDocument.Load(...);
Matches := TJsonPath.Match(Doc, ' $.store.book[*].author ' );
end ;Se você planeja usar o mesmo caminho em vários (sub) documentos, é mais rápido analisar o caminho uma vez e aplicá -lo várias vezes:
var
Doc1, Doc2: IJsonDocument;
Path: TJsonPath;
Matches1, Matches2: TArray<TJsonValue>;
begin
Doc1 := TJsonDocument.Load(...);
Doc2 := TJsonDocument.Load(...);
Path := TJsonPath.Create( ' $.store.book[*].author ' );
Matches1 := Path.Match(Doc1);
Matches2 := Path.Match(Doc2);
end ;Você também pode executar o caminho em sub-árvores:
var
Doc: IJsonDocument;
Store: TJsonValue;
Matches: TArray<TJsonValue>;
begin
Doc := TJsonDocument.Load(...);
Store := Doc.Root.Values[ ' store ' ];
Matches := TJsonPath.Match(Store, ' $.book[*].author ' );
end ; Se você estiver interessado apenas em uma única (ou a primeira), poderá usar MatchSingle :
var
Doc: IJsonDocument;
Match: TJsonValue;
begin
Doc := TJsonDocument.Load(...);
if (TJsonPath.MatchSingle(Store, ' $.book[*] ' , Match)) then
...
end ; Todo o gerenciamento de memória nesta biblioteca JSON é automático. Uma interface IJsonDocument possui todos os TJsonValue e os destrói quando o documento é destruído (sai do escopo).
A única coisa que você precisa estar ciente é que você não deve mais usar nenhum registro do TJSONVALUE depois que o documento for destruído. Fazer isso levará a comportamentos indefinidos e possivelmente falham.
Você pode personalizar algum comportamento usando estas defines condicionais:
JSON_UTF8 : usar UTF8String em vez de String em todos os lugares. Todas as cordas serão tratadas como strings UTF-8 de 8 bits, em vez de seqüências unicode de 16 bits. Isso reduz o consumo de memória e acelera um pouco. No entanto, isso significa que você também precisará usar esta biblioteca JSON com UTF8Strings, caso contrário, a Delphi converterá implicitamente entre strings Unicode e UTF8Strings, o que pode prejudicar o desempenho.JSON_STRING_INTERNING : Para ativar a internação da string para teclas de dicionário. Isso reduz o consumo de memória caso a mesma chave seja usada muitas vezes (o que é comum quando o JSON é exportado de um banco de dados), mas é um pouco mais lento. A unidade Neslib.json declara o tipo JsonString como String ou UTF8String , dependendo do JSON_UTF8 Definir. No entanto, isso não significa que você também deve usar JsonString . Se você não se importa com o JSON_UTF8 Definir, poderá usar strings regulares com esta biblioteca.
Neslib.json é licenciado sob a licença BSD simplificada.
Consulte License.txt para obter detalhes.