JSONPath e uma linguagem de consultas para JSON, da mesma forma que XPath e uma para XML. Ele foi esboado por Stefan Goessner em 2007, implementado de forma diferente por todos que precisavam dele, e finalmente padronizado como RFC 9535 em 2024. Essa historia e por que duas implementacoes de JSONPath frequentemente discordam, e por que uma folha de referencia e mais util com resultados anexados do que como uma lista de simbolos.
Cada consulta abaixo e executada contra este documento. Voce pode colar no testador JSONPath, que carrega exatamente este como amostra.
{
"store": {
"book": [
{ "category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95 },
{ "category": "fiction", "author": "Evelyn Waugh", "title": "Sword of Honour", "price": 12.99 },
{ "category": "fiction", "author": "Herman Melville", "title": "Moby Dick", "isbn": "0-553-21311-3", "price": 8.99 }
],
"bicycle": { "color": "red", "price": 19.95 }
},
"expensive": 10
}
Os seletores
Raiz. $ e o documento inteiro. Cada consulta comeca la.
Filho por nome. $.store.bicycle.color retorna "red". Um ponto seguido de um nome,
quantas vezes for necessario.
Nome entre aspas. $['store']['bicycle'] e a mesma coisa escrita de outra
maneira. Voce precisa dos colchetes quando uma chave tem espaco, ponto ou hifen, ou
quando comeca com um digito: $['first name'].
Curinga. $.store.bicycle.* retorna "red" e 19.95, todos os filhos de
esse objeto. $.store.book[*] retorna todos os tres livros.
Indice. $.store.book[0] e o primeiro livro. $.store.book[-1] e o
ultimo, contando de tras para frente.
Fatia. $.store.book[0:2] retorna os dois primeiros livros. O final e
exclusivo, exatamente como em Python. $.store.book[::2] pega cada segundo livro,
e um passo negativo inverte: $.store.book[::-1].
Uniao. $.store.book[0, 2] retorna o primeiro e terceiro livros.
$.store.bicycle['color', 'price'] seleciona dois filhos nomeados de uma vez.
Descendente. $..price retorna cada preco em qualquer profundidade, que aqui e
8.95, 12.99, 8.99 e 19.95. Este e o seletor para o qual as pessoas mais
recorrem e o que custa mais em um documento grande, porque visita cada
valor.
Filtro. $.store.book[?@.price < 10] retorna os dois livros mais baratos. O
@ se refere ao valor sendo testado. A RFC 9535 removeu os parenteses que
tutoriais mais antigos usam, entao [?@.price < 10] e [?(@.price < 10)] aparecem ambos no
meio. O testador aqui aceita ambos.
Existencia. $.store.book[?@.isbn] retorna apenas o livro que tem um campo isbn,
sem comparar com nada.
Combinando testes. &&, || e ! funcionam como voce esperaria:
$..book[?@.category == 'fiction' && @.price < 10].title retorna
"Moby Dick".
Por que um filtro que parece correto nao retorna nada
Quase sempre, e uma incompatibilidade de tipo. A especificacao diz que valores de tipos
diferentes nunca sao iguais e nunca sao ordenados, entao um preco armazenado como "8.95" com
aspas nao corresponde a @.price < 10, e tambem nao corresponde a @.price == 8.95.
Se um filtro nao retorna nada, verifique os tipos no documento antes de
verificar a consulta.
A segunda causa e anexar o filtro ao nivel errado. Um filtro escolhe entre
os filhos do valor ao qual esta anexado, entao ele vai depois do array:
book[?@.price < 10], nao book.price[?...]. Anexado a um objeto, ele testa
cada valor de propriedade desse objeto ao inves disso.
Terceiro, um valor ausente nao e null. @.isbn == null corresponde a um livro cujo
isbn e literalmente null, nao a um livro que nao tem isbn de forma alguma. Para testar
ausencia, escreva !@.isbn.
Onde as implementacoes discordam
O artigo de 2007 foi uma proposta, nao uma especificacao, entao as bibliotecas divergiram por dezessete anos. Se voce esta movendo uma consulta entre ferramentas, esses sao os lugares a verificar:
- Indices negativos e passos negativos sao amplamente suportados agora e faltam em algumas bibliotecas mais antigas.
- A forma de filtro com parenteses e a ortografia antiga. A RFC 9535 usa
[?...]nu, e muitas ferramentas aceitam apenas uma das duas. - A RFC define as funcoes
length(),count(),match()esearch(). Muitas implementacoes nao as tem ou fornecem um conjunto diferente sob nomes diferentes. - A RFC da aos resultados uma ordem de documento. Algumas implementacoes nao garantem isso, particularmente para segmentos descendentes.
O testador neste site implementa os seletores listados acima e diz isso claramente na pagina, incluindo uma tabela do que suporta. Extensões de funcoes, caminhos absolutos dentro de um filtro, e comparar dois caminhos entre si nao sao implementados, e usar um deles produz uma mensagem nomeando a funcionalidade em vez de um erro de sintaxe. Essa e uma escolha deliberada: uma ferramenta que afirma conformidade total e depois falha silenciosamente em um caso de borda e pior que uma que diz onde esta seu limite.
JSONPath ou Ponteiro JSON
Eles respondem a perguntas diferentes.
Um Ponteiro JSON nomeia exatamente um valor e resolve ou
nao resolve. Ele nao tem curingas nem filtros, e e precisamente por isso que JSON
Schema, JSON Patch e a maioria das mensagens de erro de validacao o usam: um endereco
inequivoco e o que eles precisam. /store/book/0/title significa um lugar.
Uma consulta JSONPath e uma pesquisa. Ela pode retornar muitos valores ou nenhum, e pode filtrar. Use-a quando voce nao sabe de antemao onde esta o que voce quer.
Se voce conhece o endereco, use um ponteiro. Se voce esta procurando algo, use uma consulta. Ambas as ferramentas neste site relatam a linha onde cada resultado esta sentado, entao de qualquer forma voce termina de volta no documento em vez de em uma lista abstrata de resultados.