JMESPath para principiantes: consultas de proyección que superan a JSONPath
En resumen: JMESPath reformula lotes de objetos de forma más directa que JSONPath. Esta guía para principiantes cubre sintaxis, proyecciones, pipes, funciones integradas y uso en AWS CLI.

En resumen: usa JSON Pointer para localizar por posición, JSONPath para filtrar o recorrer de forma recursiva, y JMESPath cuando necesites reformular el resultado o proyectarlo en la línea de comandos; para un único campo conocido no necesitas ninguno de los tres.
Si tu objetivo es reformular un lote de objetos en una forma distinta, JMESPath suele ser más directo que JSONPath. No busca nodos; construye un resultado.
1. Qué es JMESPath
JMESPath es un lenguaje de consulta diseñado específicamente para JSON. Se creó originalmente para filtrar la salida de AWS CLI y más tarde se extendió a muchos lenguajes mediante implementaciones como jmespath.js, jmespath.py y go-jmespath. Su objetivo difiere del de JSONPath:
- JSONPath se centra en seleccionar un conjunto de nodos por ruta y condición, y devuelve un conjunto de nodos;
- JMESPath se centra en transformar el JSON de entrada y devuelve un valor de cualquier forma.
Esta diferencia hace que JMESPath sea especialmente bueno para proyecciones de listas, renombrado de campos y recorte de resultados, por ejemplo reduciendo los veinte campos de la respuesta de una API a los tres campos que el frontend realmente necesita. Si aún no has leído lo básico de JSONPath, empieza por El lenguaje de consulta JSONPath en la práctica.
2. Sintaxis básica: de identificadores a índices
JMESPath usa menos símbolos que JSONPath y se lee más como una expresión de extracción:
| Sintaxis | Significado | Ejemplo |
|---|---|---|
name |
Acceder a un miembro de un objeto | people |
a.b |
Acceder a miembros anidados | store.bicycle.color |
[0] |
Acceder a un índice de array | people[0] |
[-1] |
Acceder al último elemento | people[-1] |
[0:2] |
Porción (slice), inicio incluido y fin excluido | people[0:2] |
[*] |
Proyección de lista | people[*].name |
[] |
Proyección de aplanado | people[].tags[] |
[?condition] |
Proyección con filtro | people[?age > 18] |
{key: expr} |
Hash de selección múltiple | {name: name, age: age} |
[expr, expr] |
Lista de selección múltiple | [name, age] |
| |
Encauzar el resultado izquierdo hacia el lado derecho | people[*].age | [0] |
Un ejemplo 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"]
Fíjate en por qué people[0:2].name devuelve un array: una porción devuelve una lista, y el .name siguiente se proyecta automáticamente sobre cada elemento de esa lista. Esta es la mayor diferencia entre JMESPath y el acceso normal a objetos.
3. Proyecciones: convertir una lista en otra
Una proyección es el concepto central de JMESPath. Funciona en dos pasos: evalúa cada elemento de una lista y luego recoge los resultados en una nueva lista. Los elementos que se evalúan como null se descartan automáticamente.
people[*].name // ["Alice", "Bob"]
people[*].{name: name, age: age} // a list of objects
people[?age > 18].name // ["Alice"]
La segunda línea es una expresión de proyección típica: people[*].{name: name, age: age}. Reconstruye cada elemento como { "name": ..., "age": ... } y descarta el campo city sobrante. En comparación con JSONPath, que te entrega nodos para que luego los mapees en código, esto ahorra un bucle.
Merece la pena distinguir dos tipos de proyección:
[*]conserva la estructura de la lista original y omite los valores null;[]es una proyección de aplanado que primero colapsa un nivel de listas anidadas, lo que encaja con expresiones comopeople[].tags[]que necesitan "todas las etiquetas de todas las personas".
4. Pipes y selección múltiple
El pipe | significa "detén la proyección actual y trata el resultado como una nueva entrada". Su uso más común es tomar un índice después de una proyección:
people[*].name | [0] → "Alice"
people[*].age | sort(@) | [-1] → the maximum age
Sin el pipe, people[*].name[0] se interpreta como tomar el primer carácter de cada nombre, que no es lo que quieres. El pipe es el símbolo más fácil de pasar por alto y más útil de JMESPath.
Hay dos formas de selección múltiple:
// Multi-select hash: the result is an object
people[0].{name: name, age: age}
// Multi-select list: the result is an array
people[0].[name, age]
Los hashes de selección múltiple son buenos para construir resultados con nombres, mientras que las listas de selección múltiple son buenas para consumir por posición. Ambas pueden anidarse dentro de proyecciones, pero atención: en un hash de selección múltiple, una clave cuyo valor es null se conserva, mientras que una proyección descarta el elemento null completo.
5. Funciones integradas
El estándar de JMESPath define un conjunto de funciones. Las comunes incluyen:
| Función | Propósito | Ejemplo |
|---|---|---|
length() |
Obtener la longitud | length(people) |
sort() |
Ordenar valores | sort(people[*].age) |
sort_by() |
Ordenar por un campo | sort_by(people, &age) |
max() / min() |
Máximo / mínimo | max(people[*].age) |
sum() |
Sumar valores | sum(people[*].age) |
keys() |
Obtener las claves de un objeto | keys(people[0]) |
contains() |
Comprobar contención | contains(name, "Ali") |
to_string() |
Convertir a cadena | to_string(age) |
El segundo argumento de sort_by es una expresión &, que significa "toma este campo de cada elemento":
sort_by(people, &age)[*].name
Estas funciones permiten que JMESPath haga agregaciones simples, mientras que JSONPath está pensado solo para seleccionar; sumar y agrupar normalmente requiere jq o código.
6. Comparación lado a lado con JSONPath
| Dimensión | JSONPath | JMESPath |
|---|---|---|
| Objetivo principal | Seleccionar nodos | Transformar datos |
| Símbolo de raíz | $ |
Ninguno; escribe los nombres de campo directamente |
| Nodo actual | @ |
@ (solo en argumentos de funciones) |
| Filtro | [?(@.age > 18)] |
[?age > 18] |
| Comodín | [*] |
[*] o [] |
| Construir objetos nuevos | No compatible | {name: name} |
| Pipes | No compatible | | |
| Estándar | RFC 9535 | JMESPath Specification |
Una guía de decisión en una frase:
- Usa JSONPath cuando solo necesites encontrar nodos que cumplan una condición;
- Usa JMESPath cuando necesites reformular el resultado en lo que espera el frontend;
- No uses ninguno cuando leas un único campo conocido; accede directamente a la propiedad.
Si el resultado de la consulta debe escribirse de vuelta en el documento, ningún lenguaje sirve. Usa JSON Pointer y parches; consulta JSON Pointer, Patch y Merge Patch.
7. JMESPath en AWS CLI
El parámetro --query de AWS CLI es JMESPath, que es su uso más extendido:
aws ec2 describe-instances \
--query 'Reservations[].Instances[].{Id: InstanceId, State: State.Name, Type: InstanceType}' \
--output table
Este comando hace tres cosas: aplana dos listas anidadas con [], conserva solo tres campos con un hash de selección múltiple y renderiza el resultado como tabla con --output table. Con JSONPath necesitarías más niveles y no obtendrías el efecto de "renombrar más recortar".
Otras formas comunes:
# Only running instances
aws ec2 describe-instances --query 'Reservations[].Instances[?State.Name==`running`].[InstanceId,PublicIpAddress]'
# The value of the Name tag
aws ec2 describe-instances --query 'Reservations[].Instances[].Tags[?Key==`Name`].Value[]'
Fíjate en que las comillas invertidas encierran literales, no cadenas. En AWS CLI, las cadenas usan comillas simples y los literales JSON usan comillas invertidas. Esta diferencia es uno de los errores de principiante más frecuentes.
8. Errores comunes
- Confundir
[*]con[]: el primero conserva el anidamiento, el segundo aplana un nivel, y las estructuras resultantes son completamente distintas; - Olvidar el pipe:
people[*].name[0]toma el primer carácter de cada nombre, no el primer nombre; - Escribir
@dentro de un filtro: los filtros de JMESPath usan los nombres de campo directamente, así que[?@.age > 18]es incorrecto; - Tratar
nullcomo una cadena vacía: las proyecciones descartannull, pero los hashes de selección múltiple conservan la clave; - Usar comillas dobles alrededor de literales en AWS CLI: el shell las expande primero y reescribe la expresión.
Consejo de depuración: valida una expresión con la entrada más pequeña posible antes de aplicarla a datos reales, y añade --output json en AWS CLI para ver los resultados sin procesar. Para la edición y el formateo de JSON del día a día, combinarlo con Editores JSON y herramientas de línea de comandos hace que localizar rutas de campo sea mucho más rápido.
Preguntas frecuentes
¿Se pueden usar JMESPath y JSONPath juntos?
Sí, pero normalmente no es necesario. Un enfoque habitual es recortar un objeto pequeño con JMESPath y luego usar el acceso normal a propiedades en el código. Si de verdad necesitas localizar por ruta y después modificar, JSONPath lee y JSON Pointer escribe, lo que es una división del trabajo más clara.
¿Por qué people[*].name devuelve algo distinto de una sola cadena?
Porque [*] es una proyección, y el resultado siempre es una lista, incluso cuando solo hay una persona. Para obtener un único valor, añade un pipe y un índice: people[*].name | [0].
¿Qué ocurre con null dentro de una proyección?
Una proyección de lista omite automáticamente los elementos que se evalúan como null, así que el resultado puede ser más corto que la lista original. Un hash de selección múltiple conserva la clave y mantiene el valor null, lo que importa al construir respuestas de API.
¿JMESPath puede hacer agregación y agrupación?
Puede hacer agregación básica con sum(), max() y sort_by(). La agrupación, las uniones y las funciones de ventana quedan fuera del estándar. Deja el análisis complejo a jq, SQL o código.
¿Cómo decido entre JMESPath y JSONPath?
Usa JSONPath cuando necesites seleccionar nodos por ruta y condición y recibir un conjunto de nodos; usa JMESPath cuando necesites reformular el resultado en lo que espera el frontend, con renombrado y recorte de campos. Cuando leas un único campo conocido, no uses ninguno; accede directamente a la propiedad.
¿Debo usar un pipe para tomar el primer valor proyectado?
Sí. people[*].name es una proyección, así que el resultado siempre es una lista; sin pipe, people[*].name[0] toma el primer carácter de cada nombre. Escribe people[*].name | [0] para obtener el primer valor.