JMESPath para iniciantes: consultas de projeção que batem o JSONPath
Resumo: JMESPath remodela lotes de objetos de forma mais direta que o JSONPath. Este guia para iniciantes cobre sintaxe, projeções, pipes, funções embutidas e uso no AWS CLI.

Resumo da ópera: use JSON Pointer para localizar por posição, JSONPath para filtrar ou descer recursivamente, e JMESPath quando precisar remodelar o resultado ou projetá-lo na linha de comando; um único campo conhecido não precisa de nenhum deles.
Se seu objetivo é remodelar um lote de objetos em uma forma diferente, o JMESPath normalmente é mais direto que o JSONPath. Ele não procura nós; ele constrói um resultado.
1. O que é JMESPath
JMESPath é uma linguagem de consulta projetada especificamente para JSON. Foi originalmente criada para filtrar a saída do AWS CLI e depois se espalhou por muitas linguagens por meio de implementações como jmespath.js, jmespath.py e go-jmespath. Seu objetivo difere do JSONPath:
- JSONPath foca em selecionar um conjunto de nós por caminho e condição, e retorna um conjunto de nós;
- JMESPath foca em transformar o JSON de entrada e retorna um valor de qualquer forma.
Essa diferença torna o JMESPath especialmente bom em projeções de lista, renomeação de campos e corte de resultados, como reduzir vinte campos de uma resposta de API aos três campos que o frontend realmente precisa. Se você ainda não leu o básico de JSONPath, comece por A linguagem de consulta JSONPath na prática.
2. Sintaxe básica: de identificadores a índices
JMESPath usa menos símbolos que o JSONPath e se lê mais como uma expressão de extração:
| Sintaxe | Significado | Exemplo |
|---|---|---|
name |
Acessar um membro de objeto | people |
a.b |
Acessar membros aninhados | store.bicycle.color |
[0] |
Acessar um índice de array | people[0] |
[-1] |
Acessar o último elemento | people[-1] |
[0:2] |
Fatia, início inclusivo e fim exclusivo | people[0:2] |
[*] |
Projeção de lista | people[*].name |
[] |
Projeção de achatamento | people[].tags[] |
[?condition] |
Projeção de filtro | people[?age > 18] |
{key: expr} |
Hash de seleção múltipla | {name: name, age: age} |
[expr, expr] |
Lista de seleção múltipla | [name, age] |
| |
Canalizar o resultado da esquerda para a direita | people[*].age | [0] |
Um exemplo mínimo:
{
"people": [
{ "name": "Alice", "age": 31, "city": "Shanghai" },
{ "name": "Bob", "age": 17, "city": "Beijing" }
]
}
people[0].name → "Alice"
people[-1].city → "Beijing"
people[0:2].name → ["Alice", "Bob"]
Observe por que people[0:2].name retorna um array: uma fatia retorna uma lista, e o .name seguinte é automaticamente projetado sobre cada elemento dessa lista. Essa é a maior diferença entre JMESPath e acesso comum a objeto.
3. Projeções: transformando uma lista em outra
Uma projeção é o conceito central em JMESPath. Funciona em duas etapas: avalia cada elemento de uma lista e depois coleta os resultados em uma nova lista. Elementos que avaliam para null são descartados automaticamente.
people[*].name // ["Alice", "Bob"]
people[*].{name: name, age: age} // uma lista de objetos
people[?age > 18].name // ["Alice"]
A segunda linha é uma expressão de projeção típica: people[*].{name: name, age: age}. Ela reconstrói cada elemento como { "name": ..., "age": ... } e descarta o campo extra city. Comparado ao JSONPath, que lhe dá nós para depois mapear no código, isso economiza um laço.
Vale distinguir dois tipos de projeção:
[*]preserva a estrutura original da lista e pula valores nulos;[]é uma projeção de achatamento que primeiro colapsa um nível de listas aninhadas, o que convém a expressões comopeople[].tags[]que precisam de "todas as tags de todas as pessoas".
4. Pipes e seleção múltipla
O pipe | significa "pare a projeção atual e trate o resultado como uma nova entrada". Seu uso mais comum é pegar um índice após uma projeção:
people[*].name | [0] → "Alice"
people[*].age | sort(@) | [-1] → a idade máxima
Sem o pipe, people[*].name[0] é interpretado como pegar o primeiro caractere de cada nome, o que não é o que você quer. O pipe é o símbolo mais facilmente negligenciado e mais útil do JMESPath.
Há duas formas de seleção múltipla:
// Hash de seleção múltipla: o resultado é um objeto
people[0].{name: name, age: age}
// Lista de seleção múltipla: o resultado é um array
people[0].[name, age]
Hashes de seleção múltipla são bons para construir resultados nomeados, enquanto listas de seleção múltipla são boas para consumo posicional. Ambos podem ser aninhados dentro de projeções, mas note: em um hash de seleção múltipla, uma chave cujo valor é null ainda é mantida, enquanto uma projeção descarta o elemento null inteiro.
5. Funções embutidas
O padrão JMESPath define um conjunto de funções. As comuns incluem:
| Função | Finalidade | Exemplo |
|---|---|---|
length() |
Obter o comprimento | length(people) |
sort() |
Ordenar valores | sort(people[*].age) |
sort_by() |
Ordenar por um campo | sort_by(people, &age) |
max() / min() |
Máximo / mínimo | max(people[*].age) |
sum() |
Somar valores | sum(people[*].age) |
keys() |
Obter chaves de objeto | keys(people[0]) |
contains() |
Verificar contenção | contains(name, "Ali") |
to_string() |
Converter para string | to_string(age) |
O segundo argumento de sort_by é uma expressão &, que significa "tome este campo de cada elemento":
sort_by(people, &age)[*].name
Essas funções permitem que o JMESPath faça agregação simples, enquanto o JSONPath é projetado apenas para seleção; somar e agrupar normalmente exigem jq ou código.
6. Uma comparação lado a lado com JSONPath
| Dimensão | JSONPath | JMESPath |
|---|---|---|
| Objetivo principal | Selecionar nós | Transformar dados |
| Símbolo raiz | $ |
Nenhum; escreva nomes de campo diretamente |
| Nó atual | @ |
@ (apenas em argumentos de função) |
| Filtro | [?(@.age > 18)] |
[?age > 18] |
| Wildcard | [*] |
[*] ou [] |
| Construir novos objetos | Não suportado | {name: name} |
| Pipes | Não suportado | | |
| Padrão | RFC 9535 | JMESPath Specification |
Um guia de decisão em uma frase:
- Use JSONPath quando você só precisa encontrar nós que correspondem a uma condição;
- Use JMESPath quando precisar remodelar o resultado no que o frontend espera;
- Não use nenhum dos dois quando estiver lendo um único campo conhecido; apenas acesse a propriedade diretamente.
Se o resultado da consulta precisa ser gravado de volta no documento, nenhuma das linguagens serve. Use JSON Pointer e patches; veja JSON Pointer, Patch e Merge Patch.
7. JMESPath no AWS CLI
O parâmetro --query do AWS CLI é JMESPath, que é seu caso de uso mais difundido:
aws ec2 describe-instances \
--query 'Reservations[].Instances[].{Id: InstanceId, State: State.Name, Type: InstanceType}' \
--output table
Este comando faz três coisas: achata duas listas aninhadas com [], mantém apenas três campos com um hash de seleção múltipla e renderiza o resultado como tabela com --output table. Com JSONPath você precisaria de mais níveis e não obteria o efeito de "renomear mais cortar".
Outras formas comuns:
# Apenas instâncias em execução
aws ec2 describe-instances --query 'Reservations[].Instances[?State.Name==`running`].[InstanceId,PublicIpAddress]'
# O valor da tag Name
aws ec2 describe-instances --query 'Reservations[].Instances[].Tags[?Key==`Name`].Value[]'
Observe que crases delimitam literais, não strings. No AWS CLI, strings usam aspas simples e literais JSON usam crases. Essa diferença é um dos erros de iniciante mais comuns.
8. Erros comuns
- Confundir
[*]e[]: o primeiro preserva o aninhamento, o segundo achata um nível, e as estruturas resultantes são completamente diferentes; - Esquecer o pipe:
people[*].name[0]pega o primeiro caractere de cada nome, não o primeiro nome; - Escrever
@dentro de um filtro: filtros JMESPath usam nomes de campo diretamente, então[?@.age > 18]está errado; - Tratar
nullcomo string vazia: projeções descartamnull, mas hashes de seleção múltipla mantêm a chave; - Usar aspas duplas em literais no AWS CLI: o shell as expande primeiro e reescreve a expressão.
Conselho de depuração: valide uma expressão na menor entrada possível antes de aplicá-la a dados reais, e acrescente --output json no AWS CLI para ver resultados crus. Para edição e formatação JSON do dia a dia, combinar com Editores JSON e ferramentas de linha de comando torna a localização de caminhos de campo muito mais rápida.
Perguntas frequentes
JMESPath e JSONPath podem ser usados juntos?
Sim, mas normalmente é desnecessário. Uma abordagem comum é cortar um objeto pequeno com JMESPath e depois usar acesso simples a propriedade no código. Se você realmente precisa localizar por caminho e depois modificar, o JSONPath lê e o JSON Pointer grava, o que é uma divisão de trabalho mais clara.
Por que people[*].name retorna algo diferente de uma string única?
Porque [*] é uma projeção, e o resultado é sempre uma lista, mesmo quando há apenas uma pessoa. Para obter um valor único, acrescente um pipe e um índice: people[*].name | [0].
O que acontece com null dentro de uma projeção?
Uma projeção de lista pula automaticamente elementos que avaliam para null, então o resultado pode ser mais curto que a lista original. Um hash de seleção múltipla mantém a chave e preserva o valor null, o que importa ao construir respostas de API.
JMESPath consegue fazer agregação e agrupamento?
Consegue fazer agregação básica com sum(), max() e sort_by(). Agrupamento, joins e funções de janela estão fora do padrão. Deixe análise complexa para jq, SQL ou código.
Como decido entre JMESPath e JSONPath?
Use JSONPath quando precisar selecionar nós por caminho e condição e receber um conjunto de nós; use JMESPath quando precisar remodelar o resultado no que o frontend espera, com renomeação e corte de campos. Quando estiver lendo um único campo conhecido, não use nenhum dos dois; apenas acesse a propriedade.
Devo usar um pipe para pegar o primeiro valor projetado?
Sim. people[*].name é uma projeção, então o resultado é sempre uma lista; sem um pipe, people[*].name[0] pega o primeiro caractere de cada nome. Escreva people[*].name | [0] para obter o primeiro valor.