JSONPath Tutorial: Query JSON Like a Pro
Master JSONPath syntax with practical examples. Learn every operator, filter expression, and real-world query pattern to extract data from complex JSON structures.
¿Qué es JSONPath?
JSONPath es un lenguaje de consulta para JSON, similar a lo que XPath hace para XML. Te permite acceder a una estructura JSON compleja y extraer exactamente los datos que necesitas utilizando una sintaxis de expresión concisa. En lugar de escribir bucles y condicionales para explorar objetos y arreglos anidados, escribes una única expresión de ruta como $.store.book[?(@.price < 10)].title y obtienes de vuelta cada título de libro que cueste menos de diez dólares.
Stefan Goessner introdujo JSONPath en 2007, y desde entonces se ha convertido en un estándar de facto en diversos lenguajes y herramientas. Si trabajas con APIs REST, archivos de configuración o cualquier dato JSON no trivial, JSONPath te ahorrará un tiempo significativo.
JSONPath vs. XPath: Una Comparación Rápida
Si ya conoces XPath, JSONPath te resultará familiar. La principal diferencia es que JSON tiene un modelo de datos más simple: objetos y arreglos en lugar de elementos, atributos y espacios de nombres. Aquí te mostramos cómo se comparan ambos:
| Concepto | XPath | JSONPath |
|---|---|---|
| Raíz | / | $ |
| Hijo | /child | .child o ['child'] |
| Descenso recursivo | // | .. |
| Comodín | * | * |
| Índice de arreglo | [1] (1-based) | [0] (0-based) |
| Filtro | [predicate] | [?(expression)] |
La Referencia Completa de Operadores JSONPath
Cada expresión JSONPath comienza desde la raíz $ y encadena operadores para navegar más profundo. Aquí están todos los operadores que necesitas conocer:
| Operador | Descripción | Ejemplo |
|---|---|---|
$ | El objeto o arreglo raíz | $ — el documento completo |
.key | Miembro hijo por nombre (notación de punto) | $.store.name |
['key'] | Miembro hijo por nombre (notación de corchetes) | $['store']['name'] |
.. | Descenso recursivo — busca todos los descendientes | $..price — cada campo "price" |
* | Comodín — todos los miembros de un objeto o arreglo | $.store.* |
[n] | Índice de arreglo (0-based) | $.items[0] |
[start:end] | Porción de arreglo (el final es exclusivo) | $.items[0:3] — los primeros tres elementos |
[start:end:step] | Porción de arreglo con paso | $.items[::2] — cada segundo elemento |
[n,m] | Múltiples índices de arreglo | $.items[0,2,4] |
[?()] | Expresión de filtro | $.items[?(@.price > 10)] |
@ | Nodo actual (utilizado dentro de filtros) | @.name == 'Alice' |
JSON de Ejemplo para Nuestros Ejemplos
Usaremos este documento JSON a lo largo del tutorial. Representa la respuesta de una pequeña API de librería:
{
"store": {
"name": "TechBooks Online",
"book": [
{
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95,
"inStock": true
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99,
"inStock": false
},
{
"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99,
"inStock": true
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99,
"inStock": true
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
}
}
Ejemplos Prácticos de JSONPath
1. Obtener el Nombre de la Tienda
$.store.name
Resultado:
"TechBooks Online"
2. Obtener Todos los Títulos de Libros
$.store.book[*].title
Resultado:
[
"Sayings of the Century",
"Sword of Honour",
"Moby Dick",
"The Lord of the Rings"
]
3. Obtener el Primer Libro
$.store.book[0]
Resultado: el objeto completo del primer libro.
4. Obtener el Último Libro
$.store.book[-1]
Los índices negativos cuentan desde el final. -1 te da el último elemento.
5. Porción: Primeros Dos Libros
$.store.book[0:2]
Devuelve los libros en los índices 0 y 1. El índice final es exclusivo, al igual que en slice() de Python y JavaScript.
6. Obtener Todos los Precios en Todo el Documento
$..price
Resultado:
[8.95, 12.99, 8.99, 22.99, 19.95]
El operador de descenso recursivo .. encuentra cada campo price sin importar la profundidad, incluyendo el precio de la bicicleta.
7. Filtro: Libros por Debajo de $10
$.store.book[?(@.price < 10)]
Devuelve los dos libros con precios de 8.95 y 8.99. El símbolo @ se refiere al elemento actual que se está evaluando.
8. Filtro: Libros que Tienen un ISBN
$.store.book[?(@.isbn)]
Devuelve solo Moby Dick y El Señor de los Anillos.
9. Filtro: Libros de Ficción en Stock
$.store.book[?(@.category == 'fiction' && @.inStock == true)]
Puedes combinar condiciones con && (y) y || (o).
10. Múltiples Índices
$.store.book[0,3]
Selecciona elementos específicos. Devuelve el primer y cuarto libro.
11. Comodín en Objeto
$.store.bicycle.*
Resultado:
["red", 19.95]
Devuelve todos los valores del objeto bicicleta.
12. Obtener Autores de Libros Caros
$.store.book[?(@.price > 15)].author
Resultado:
["J. R. R. Tolkien"]
Inténtalo tú mismo: Pega el JSON de ejemplo arriba y experimenta con estas expresiones en nuestro JSONPath Tester.
Expresiones de Filtro en Profundidad
Las expresiones de filtro son la parte más poderosa de JSONPath. Viven dentro de [?()] y evalúan una condición booleana para cada elemento. Aquí están los operadores que puedes usar:
| Operador | Significado | Ejemplo |
|---|---|---|
== | Igual a | @.status == 'active' |
!= | No igual a | @.role != 'admin' |
> | Mayor que | @.age > 18 |
>= | Mayor o igual que | @.score >= 90 |
< | Menor que | @.price < 50 |
<= | Menor o igual que | @.quantity <= 0 |
=~ | Coincidencia regex (algunas implementaciones) | @.name =~ /^J.*/ |
&& | Y lógico | @.price > 5 && @.price < 20 |
| ` | ` |
Un patrón común es verificar la existencia de propiedades. Escribir @.isbn sin un operador de comparación devuelve true si la propiedad existe y no es null.
Escenarios del Mundo Real
Extraer Todos los Correos Electrónicos de una Respuesta de API
Supongamos que recibes una respuesta de una API de gestión de usuarios con objetos de contacto profundamente anidados:
{
"users": [
{
"name": "Alice",
"contacts": { "email": "alice@example.com", "phone": "555-0101" }
},
{
"name": "Bob",
"contacts": { "email": "bob@example.com", "phone": "555-0102" }
}
]
}
Consulta: $..email
Resultado:
["alice@example.com", "bob@example.com"]
El operador de descenso recursivo encuentra cada campo email sin importar dónde se encuentre en la jerarquía.
Encontrar Productos por Rango de Precio
$.products[?(@.price >= 25 && @.price <= 100)]
Útil al filtrar respuestas de API del lado del cliente antes de renderizar una lista de productos.
Obtener Valores de Configuración Anidados
$.config.database.connections[0].host
Profundiza en archivos de configuración sin escribir acceso opcional encadenado como config?.database?.connections?.[0]?.host.
JSONPath en Diferentes Lenguajes
JavaScript / Node.js
El paquete jsonpath-plus es la implementación más popular:
import { JSONPath } from "jsonpath-plus";
const data = { store: { book: [{ title: "Moby Dick", price: 8.99 }] } };
const titles = JSONPath({ path: "$.store.book[*].title", json: data });
console.log(titles); // ["Moby Dick"]
Python
Usa jsonpath-ng para una implementación conforme a los estándares:
from jsonpath_ng.ext import parse
data = {"store": {"book": [{"title": "Moby Dick", "price": 8.99}]}}
expr = parse("$.store.book[*].title")
matches = [match.value for match in expr.find(data)]
print(matches) # ['Moby Dick']
Java
La biblioteca Jayway JSONPath es la opción preferida para proyectos en JVM:
import com.jayway.jsonpath.JsonPath;
String json = "{\"store\":{\"book\":[{\"title\":\"Moby Dick\",\"price\":8.99}]}}";
List<String> titles = JsonPath.read(json, "$.store.book[*].title");
System.out.println(titles); // [Moby Dick]
Problemas y Limitaciones
JSONPath es poderoso, pero hay algunas cosas a tener en cuenta antes de confiar en él en producción:
- Sin especificación estándar (hasta hace poco). JSONPath fue definido de manera informal, y diferentes bibliotecas lo implementaron con variaciones sutiles. El IETF publicó el RFC 9535 en 2024 para estandarizar la sintaxis, pero muchas bibliotecas aún siguen la especificación original de Goessner. Siempre verifica la documentación de tu biblioteca para conocer las características soportadas.
- El soporte de regex varía. El operador de regex
=~no es universalmente soportado. La biblioteca Java de Jayway lo soporta; muchas bibliotecas de JavaScript no. - Sin operaciones de escritura. JSONPath es solo de lectura. No puedes usarlo para modificar el documento original. Para mutaciones, necesitas JSON Patch (RFC 6902) o un recorrido manual.
- Rendimiento en documentos grandes. El descenso recursivo (
..) escanea cada nodo en el árbol. En documentos con millones de nodos, esto puede ser lento. Prefiere rutas específicas cuando el rendimiento es importante. - Inconsistencia en el tipo de retorno. Algunas bibliotecas devuelven un solo valor para coincidencias únicas y un arreglo para coincidencias múltiples. Otras siempre devuelven un arreglo. Normaliza el tipo de retorno en tu código para evitar sorpresas.
- Caracteres especiales en las claves. Si tus claves JSON contienen puntos, espacios u otros caracteres especiales, usa la notación de corchetes:
$('my.key')en lugar de$.my.key.
JSONPath vs. jq
Podrías preguntarte cómo se compara JSONPath con jq, el popular procesador de JSON en línea de comandos. La respuesta corta: resuelven problemas similares pero para diferentes audiencias.
- JSONPath está diseñado para ser embebido en el código de la aplicación. Tiene soporte de biblioteca en todos los lenguajes principales y una sintaxis de consulta sencilla.
- jq es un lenguaje de transformación de datos completo. Puede filtrar, mapear, reducir y remodelar JSON. Es más poderoso pero tiene una curva de aprendizaje más pronunciada y se utiliza principalmente en la línea de comandos.
Para consultar datos dentro de una aplicación, JSONPath es la opción pragmática. Para scripting en shell y tuberías de datos, jq es difícil de superar.
Hoja de Referencia Rápida
| Lo que quieres | JSONPath |
|---|---|
| Documento completo | $ |
| Propiedad específica | $.user.name |
| Todos los elementos en un arreglo | $.items[*] |
| Primer elemento | $.items[0] |
| Último elemento | $.items[-1] |
| Rango de elementos | $.items[1:4] |
| Cada enésimo elemento | $.items[::2] |
| Todos los campos "name" en cualquier lugar | $..name |
| Filtrar por valor | $.items[?(@.price < 10)] |
| Filtrar por existencia | $.items[?(@.discount)] |
| Múltiples condiciones | $.items[?(@.qty > 0 && @.active)] |
Inténtalo tú mismo: Abre el JSONPath Tester para ejecutar consultas contra tus propios datos JSON en tiempo real.