Guía de sintaxis JSONPath: consultar y filtrar JSON con ejemplos
JSONPath es un lenguaje de consulta para JSON, tal como XPath lo es para XML. Escribes una expresión de ruta y el evaluador devuelve todos los valores que coinciden. Para obtener todos los nombres de autor de un documento de librería, escribes $.store.book[*].author y recibes la lista de autores, sin necesidad de código de recorrido.
Esta guía recorre cada selector de la sintaxis JSONPath con ejemplos listos para copiar que puedes ejecutar mientras lees. Conviene aclarar algo desde el principio: existen dos dialectos. El dialecto Goessner de 2007 es el clásico de facto, y RFC 9535 es el estándar formal del IETF publicado en febrero de 2024. Coinciden en las rutas comunes y divergen en los casos límite, así que esta guía señala las diferencias a medida que aparecen. Puedes probar cada expresión de abajo en el JSONPath Tester y alternar entre ambos motores para compararlos.
La hoja de referencia de selectores va primero. El resto del artículo desarrolla cada fila con un ejemplo práctico sobre un mismo documento JSON compartido.
| Selector | Significado | Ejemplo |
|---|---|---|
$ | Raíz del documento | $ |
@ | Elemento actual (en filtros) | [?(@.price < 10)] |
.name / ['name'] | Miembro hijo | $.store.book |
.. | Descenso recursivo | $..author |
* | Todos los elementos / miembros | $.store.book[*] |
[0] | Índice de arreglo | $.store.book[0] |
[start:end:step] | Segmento de arreglo (semiabierto) | $.store.book[0:2] |
[a,b] | Unión de nombres / índices | $.store.book[0,2] |
[?()] | Expresión de filtro | $.store.book[?(@.price < 10)] |
length() count() match() search() value() | Funciones de RFC 9535 (solo en filtros) | [?length(@.title) > 15] |
¿Qué es JSONPath?
JSONPath es un lenguaje de consulta declarativo para seleccionar nodos dentro de un documento JSON. En lugar de escribir un bucle que recorra objetos y arreglos, describes la ubicación que quieres mediante una ruta, y el evaluador devuelve los valores coincidentes. El modelo mental es el mismo que XPath ofrece para XML: una ruta hecha de selectores que avanzan por la estructura.
Aparece en todas partes donde los desarrolladores tocan JSON. Lo usas para extraer un campo de una respuesta de API, para hacer aserciones sobre un valor en una prueba de integración, para direccionar campos en configuraciones de pipelines para Kubernetes, AWS Step Functions y Azure Logic Apps, y para extraer datos de JSON grande o irregular sin escribir a mano la lógica de recorrido.
Un breve apunte histórico, porque explica la división de dialectos. Stefan Goessner propuso JSONPath en 2007. Se difundió rápido y se convirtió en un estándar de facto, pero nunca se especificó formalmente, así que las implementaciones se fueron separando en los detalles. El IETF cerró esa brecha en febrero de 2024 con RFC 9535, la primera especificación formal de JSONPath. Ambos dialectos siguen vivos hoy, y por eso la misma expresión puede comportarse de forma distinta según la biblioteca que la ejecute.
Antes de empezar a consultar, conviene leer la estructura. Formatea una entrada desordenada con el JSON Formatter para que el anidamiento quede visible.
El documento de ejemplo
Todos los ejemplos de abajo se ejecutan sobre el clásico JSON de librería de Goessner. Pégalo una vez y reutilízalo:
{
"store": {
"book": [
{ "title": "Sayings of the Century", "author": "Nigel Rees", "price": 8.95 },
{ "title": "Sword of Honour", "author": "Evelyn Waugh", "price": 12.99 },
{ "title": "Moby Dick", "author": "Herman Melville", "price": 8.99 },
{ "title": "The Lord of the Rings", "author": "J. R. R. Tolkien", "price": 22.99 }
],
"bicycle": { "color": "red", "price": 19.95 }
}
}
Cuatro libros con título, autor y precio, más una bicicleta. Ten esto presente: los precios son 8.95, 12.99, 8.99 y 22.99, y eso determina los resultados de los filtros más adelante.
Raíz, hijo y descenso recursivo ($ . ..)
Toda expresión empieza en la raíz, que se escribe $. Desde ahí accedes a los hijos con un punto o con notación de corchetes; las dos son equivalentes:
$.store.book → the book array
$['store']['book'] → identical result
La notación de corchetes es la que necesitas cuando una clave tiene espacios, puntos u otros caracteres especiales: $['first name'] funciona donde $.first name no lo haría.
El operador .. es el descenso recursivo. Busca en todos los niveles del documento, no solo en los hijos directos, y reúne todo lo que coincida con el selector que le sigue a cualquier profundidad:
$..author
→ ["Nigel Rees", "Evelyn Waugh", "Herman Melville", "J. R. R. Tolkien"]
Cuándo usar .. en vez de una ruta completa
El descenso recursivo es cómodo, pero tosco. $..price coincide con todos los precios en cualquier parte del árbol, incluido store.bicycle.price, que quizá no querías. Cuando conoces la forma, escribe la ruta completa para que la consulta siga siendo precisa:
$..price → [8.95, 12.99, 8.99, 22.99, 19.95] (includes the bicycle)
$.store.book[*].price → [8.95, 12.99, 8.99, 22.99] (only books)
Reserva .. para estructuras realmente irregulares o desconocidas. El intercambio es comodidad frente a control: cuanto más sepas sobre tus datos, más deberías preferir una ruta explícita.
Comodines, índices y segmentos de arreglo (* [0] [start:end:step])
El comodín * selecciona todos los elementos de un arreglo o todos los miembros de un objeto:
$.store.book[*].title
→ ["Sayings of the Century", "Sword of Honour", "Moby Dick", "The Lord of the Rings"]
Los índices de arreglo empiezan en cero, y los índices negativos cuentan desde el final:
$.store.book[0].title → ["Sayings of the Century"]
$.store.book[-1].title → ["The Lord of the Rings"]
Los segmentos usan la misma convención semiabierta [start:end:step] que Python y JavaScript: start se incluye, end se excluye.
$.store.book[0:2].title → ["Sayings of the Century", "Sword of Honour"]
Eso devuelve dos libros, no tres: el índice 0 y el índice 1, deteniéndose antes del índice 2. El límite final excluyente es uno de los errores de JSONPath más comunes, así que conviene tenerlo claro:
[0:2] → first TWO elements (indices 0, 1) ← correct
[0:3] → first THREE elements (indices 0, 1, 2)
Omite un límite para llegar hasta el borde, y añade un paso para tomar cada N-ésimo elemento:
$.store.book[2:].title → ["Moby Dick", "The Lord of the Rings"]
$.store.book[:3].title → first three titles
$.store.book[::2].title → ["Sayings of the Century", "Moby Dick"] (every other)
Expresiones de filtro [?()]
Los filtros son la parte más útil de JSONPath. Un filtro [?()] conserva solo los elementos para los que un predicado es verdadero, y dentro del filtro, @ se refiere al elemento actual que se está evaluando.
Para seleccionar los libros que cuestan menos de 10:
$.store.book[?(@.price < 10)].title
→ ["Sayings of the Century", "Moby Dick"]
Frente a los precios de la librería (8.95, 12.99, 8.99, 22.99), dos libros califican. Así se construye un predicado de filtro paso a paso:
- Compara contra un literal. Usa
==,!=,<,<=,>,>=; por ejemplo@.price > 10. - Coincide con una cadena. Los literales de cadena llevan comillas simples:
@.author == 'Nigel Rees'. - Comprueba la existencia. Una referencia simple a un miembro selecciona los elementos que lo tienen:
[?(@.isbn)]conserva solo los libros con unisbn. - Combina condiciones. Une predicados con
&&y||:[?(@.price < 10 && @.author == 'Herman Melville')].
El error más frecuente en los filtros es el alcance de @. Dentro del predicado, el elemento actual es @, no $. Escribir $.price apunta de vuelta a la raíz del documento, no al libro que se está evaluando:
$.store.book[?($.price < 10)] → wrong scope, matches nothing useful
$.store.book[?(@.price < 10)] → correct: each book's own price
RFC 9535 frente a Classic en los filtros
Los dos dialectos se separan en el manejo de los espacios en blanco y las comillas. Classic es permisivo: [?(@.price<10)] sin espacios se analiza sin problemas. RFC 9535 sigue su gramática al pie de la letra y es más estricto sobre cómo se escribe el filtro. Si un filtro que funcionaba en otro lado falla, revisa el espaciado y el motor. Mantén los filtros limpios (operadores con espacios, cadenas entre comillas simples) y se evaluarán igual sin importar qué biblioteca acabe ejecutándolos.
Selectores de unión: seleccionar varias claves a la vez ([a,b])
Un selector de unión lista varios nombres o índices dentro de un mismo corchete y los reúne todos:
$.store.book[0]['title','author']
→ ["Sayings of the Century", "Nigel Rees"]
Las uniones también funcionan con índices, y puedes mezclarlas con otros selectores para una proyección fija:
$.store.book[0,2].title → ["Sayings of the Century", "Moby Dick"]
$.store.book[*]['title','price'] → title and price of every book
Las uniones son la herramienta adecuada cuando quieres unos pocos campos concretos en lugar de un objeto completo o un barrido con comodín.
Funciones de RFC 9535: length, count, match, search, value
RFC 9535 define cinco extensiones de función estándar. Hay una regla que se malinterpreta a menudo:
Estas funciones solo se pueden llamar dentro de un filtro
[?...], nunca como un segmento de ruta independiente.
Escribir $.store.book.length() no es RFC 9535 válido, y la gramática estándar lo rechaza. Esa forma de llamada como segmento es una extensión de jsonpath-plus, no parte de la especificación. Para filtrar por longitud, llamas a la función dentro del predicado:
$.store.book[?length(@.title) > 15]
→ [
{ "title": "Sayings of the Century", "author": "Nigel Rees", "price": 8.95 },
{ "title": "The Lord of the Rings", "author": "J. R. R. Tolkien", "price": 22.99 }
]
Ambos títulos seleccionados tienen más de 15 caracteres; “Moby Dick” (9) y “Sword of Honour” (15, no más de 15) quedan excluidos.
Esto es lo que hace cada función dentro de un filtro:
length()— longitud de una cadena, arreglo u objeto:[?length(@.title) > 15]count()— número de nodos en una lista de nodos:[?(count(@.authors) > 1)]match()— prueba de regex sobre la cadena completa (patrón I-Regexp):[?match(@.author, 'J.*')]search()— prueba de regex sobre una subcadena:[?search(@.title, 'the')]value()— convierte una lista de un solo nodo en su valor para compararlo
Las cinco son una característica de RFC 9535. El dialecto Classic (Goessner) no las implementa, así que si una expresión basada en funciones falla, confirma que la estás llamando dentro de un filtro y que tu motor está configurado en RFC 9535.
RFC 9535 frente a Classic Goessner: por qué la misma expresión difiere
Cuando una expresión JSONPath devuelve resultados distintos en dos herramientas, el dialecto suele ser la causa. Así se comparan los dos:
| Aspecto | Classic Goessner (2007) | RFC 9535 (2024) |
|---|---|---|
| Estandarización | De facto, nunca formalizado | Primera especificación formal del IETF |
| Espacios/comillas en filtros | Permisivo ([?(@.price<10)] válido) | Estricto, sigue la gramática al pie de la letra |
| Comparación de miembro ausente | Depende de la implementación | Bien definida, no lanza error |
| Funciones estándar | No forman parte del dialecto | length count match search value |
| Rutas normalizadas | Sin forma canónica | Canónica, forma de corchetes con comillas simples |
| Orden de la unión | Varía según la biblioteca | Especificado |
Consejo práctico: si tu sistema posterior anuncia compatibilidad con RFC 9535, escribe y valida contra el motor estándar. Si mantienes una expresión copiada de jsonpath.com, jsonpath-plus o un servicio basado en Jayway, usa Classic para que los resultados se reproduzcan. El JSONPath Tester ejecuta ambos motores detrás de un mismo conmutador, así que puedes pegar una expresión una sola vez y ver cómo la maneja cada dialecto en paralelo. Esa comparación lado a lado es la forma más rápida de diagnosticar una divergencia.
JSONPath frente a XPath frente a jq: cuál usar
Estos tres se confunden a menudo, así que aquí va la versión corta:
- JSONPath es una consulta de ruta declarativa para JSON. Funciona mejor incrustada en configuraciones y aserciones de prueba, donde quieres nombrar una ubicación sin escribir código.
- XPath es el equivalente del mundo XML. JSONPath tomó prestada parte de su notación (
*,..,[]), por eso la analogía se sostiene, pero los lenguajes no son intercambiables y sus conjuntos de funciones difieren. - jq es un procesador de JSON de línea de comandos. Va mucho más allá de la selección de rutas (transforma, agrega y reestructura datos) y vive en el pipeline de tu shell.
La decisión suele ser clara. Para una aserción incrustada o un campo de configuración de pipeline, recurre a JSONPath. Para transformación y manipulación de datos desde el shell, recurre a jq; la hoja de referencia de jq cubre ese flujo de trabajo en profundidad. Y cuando la pregunta es si una carga útil se ajusta a una forma esperada en vez de dónde vive un campo, valídala con el JSON Schema Validator y su guía completa de validación.
7 errores comunes de JSONPath
- Olvidar la raíz
$. La mayoría de los motores rechazanstore.book; toda expresión empieza en$. - Error de uno en el segmento.
[0:2]son dos elementos, no tres: el límite final es excluyente. - Dialecto equivocado. Ejecutar una expresión Classic bajo RFC 9535 (o al revés) puede provocar un error de análisis o coincidir con nodos distintos. Cambia el motor para que coincida.
- Función como segmento independiente.
$.store.book.length()no es RFC 9535 válido; llama alength()dentro de un filtro. - Olvidar
@en un filtro.[?($.price < 10)]apunta a la raíz; usa[?(@.price < 10)]. - Comillas de corchete incorrectas.
$[store]es un error; pon la clave entre comillas:$['store']. - Suponer que
..se detiene en el primer nivel. El descenso recursivo coincide a cualquier profundidad, no solo en los hijos directos.
Prueba JSONPath en línea, de forma privada
La forma más rápida de aprender la sintaxis JSONPath es ejecutarla. El JSONPath Tester evalúa en vivo cada expresión de esta guía: motores duales RFC 9535 y Classic, vistas de resultado Valores / Rutas / Ambas, rutas normalizadas para depurar, y ejecución 100% en el navegador, sin subida de archivos, sin registro y sin eval, así que es seguro para cargas útiles propietarias. Construye una ruta aquí, confirma que selecciona exactamente los nodos que quieres y luego pega la expresión validada directo en tu código, pruebas o pipeline.
Para el resto del flujo de trabajo con JSON, convierte una respuesta de muestra en interfaces tipadas con JSON to TypeScript, o compara dos documentos campo por campo con JSON Diff.
Preguntas frecuentes
¿Para qué se usa JSONPath?
JSONPath consulta JSON sin código imperativo. Los desarrolladores lo usan para extraer campos de respuestas de API, hacer aserciones sobre valores en pruebas de integración y direccionar campos en configuraciones para Kubernetes, AWS Step Functions y Azure Logic Apps. Brilla al extraer datos de estructuras grandes o irregulares, donde escribir un recorrido a mano sería tedioso.
¿Cuál es la diferencia entre RFC 9535 y JSONPath clásico?
Classic es el dialecto de facto de Stefan Goessner de 2007: ampliamente implementado pero nunca especificado formalmente, así que las bibliotecas divergieron. RFC 9535 es la especificación formal del IETF de febrero de 2024: define una gramática precisa, rutas normalizadas para los resultados y cinco funciones estándar. Los dos difieren en los bordes: filtros, uniones y comparación de miembros ausentes.
¿Cómo funcionan las expresiones de filtro de JSONPath?
Un filtro [?()] conserva solo los elementos cuyo predicado es verdadero, y @ es el elemento actual. Por ejemplo, $.store.book[?(@.price < 10)] selecciona los libros con precio menor que 10. Puedes combinar condiciones con && y ||, comprobar si existe un miembro y comparar contra literales de cadena o número.
¿Puedo usar length() como $.store.book.length()?
No. En RFC 9535, length() y las otras cuatro funciones solo se pueden llamar dentro de un filtro, como $.store.book[?length(@.title) > 15]. La forma de segmento independiente $.store.book.length() es una extensión de jsonpath-plus, no JSONPath estándar, y la gramática de RFC 9535 la rechaza.
¿Es JSONPath lo mismo que XPath?
No, pero la idea es similar. XPath consulta XML; JSONPath consulta JSON; ambos localizan nodos con selectores de ruta. JSONPath tomó prestada deliberadamente parte de la notación de XPath (*, .. y []), lo que hace útil la analogía, pero la sintaxis, la semántica y los conjuntos de funciones son distintos y no intercambiables.
¿Qué hace el descenso recursivo (..) en JSONPath?
El operador .. busca en todos los niveles del documento, no solo en los hijos directos. $..author reúne todos los miembros author dondequiera que aparezcan, a cualquier profundidad de anidamiento. Es la forma más rápida de extraer un campo de una estructura profundamente anidada o irregular, pero puede coincidir con muchos más nodos de los que esperas; acótalo cuando puedas.