Si alguna vez te has quedado mirando una respuesta de API profundamente anidada pensando "solo necesito ese campo enterrado ahí" — JSONPath es la herramienta que buscas. Es un lenguaje de consulta para JSON, similar en espíritu a cómo XPath funciona para XML. Escribes una expresión de ruta, obtienes los valores coincidentes. Sin bucles, sin recorrido manual. Stefan Goessner lo introdujo en 2007, y después de años de implementaciones ligeramente incompatibles, fue oficialmente estandarizado como RFC 9535 en 2024. También puedes explorar expresiones JSONPath de forma interactiva con la herramienta JSON Path.
El conjunto de datos que usaremos a lo largo del artículo
En lugar de ejemplos abstractos, trabajemos con algo realista — una respuesta de pedido de e-commerce. Este es el tipo de JSON que obtendrías de una API de gestión de pedidos:
{
"order": {
"id": "ORD-9182",
"status": "shipped",
"customer": {
"id": "CUST-441",
"name": "Maria Chen",
"email": "[email protected]"
},
"shippingAddress": {
"street": "14 Maple Avenue",
"city": "Portland",
"state": "OR",
"zip": "97201",
"country": "US"
},
"lineItems": [
{
"sku": "WH-1042",
"name": "Wireless Headphones",
"qty": 1,
"unitPrice": 89.99,
"inStock": true
},
{
"sku": "CB-USB-C",
"name": "USB-C Charging Cable",
"qty": 2,
"unitPrice": 12.49,
"inStock": true
},
{
"sku": "SC-PRO-7",
"name": "Phone Stand Pro",
"qty": 1,
"unitPrice": 34.00,
"inStock": false
}
],
"totals": {
"subtotal": 148.97,
"shipping": 5.99,
"tax": 11.92,
"total": 166.88
}
}
}El operador raíz $ y la navegación básica
Cada expresión JSONPath comienza con $, que hace referencia a la raíz del documento.
Desde ahí, navegas usando puntos. ¿Quieres el estado del pedido? Es sencillo:
$.order.status
// → "shipped"
$.order.customer.name
// → "Maria Chen"
$.order.totals.total
// → 166.88
$.order.shippingAddress.city
// → "Portland"También puedes usar la notación de corchetes, que es útil cuando una clave contiene espacios o caracteres especiales, o cuando accedes a elementos de un array por índice. Ambos estilos son válidos según las reglas de sintaxis del RFC 9535:
// Notación de puntos
$.order.customer.email
// Notación de corchetes — equivalente
$['order']['customer']['email']
// Índice de array — primer elemento de línea
$.order.lineItems[0].name
// → "Wireless Headphones"
// Último elemento de línea (indexación negativa — compatible con RFC 9535)
$.order.lineItems[-1].name
// → "Phone Stand Pro"$[-1:] (sintaxis de slice) y
$.array[-1] (índice negativo directo) son válidos en RFC 9535, pero algunas implementaciones antiguas
no los soportan. Prueba contra tu biblioteca específica si usas índices negativos.Comodines — Consultar todos los hijos a la vez
El comodín * coincide con todos los elementos en un nivel dado. En lugar de pedir
lineItems[0], lineItems[1], etc., puedes obtenerlos todos a la vez:
// Todos los nombres de artículos
$.order.lineItems[*].name
// → ["Wireless Headphones", "USB-C Charging Cable", "Phone Stand Pro"]
// Todos los precios unitarios de artículos
$.order.lineItems[*].unitPrice
// → [89.99, 12.49, 34.00]
// Todos los SKU de artículos
$.order.lineItems[*].sku
// → ["WH-1042", "CB-USB-C", "SC-PRO-7"]Este es el patrón que usarás con más frecuencia al trabajar con arrays en respuestas de API. En lugar de mapear sobre un array en el código de tu aplicación, puedes extraer exactamente el campo que necesitas antes de que llegue a tu lógica de negocio.
Descenso recursivo — Encontrar valores a cualquier profundidad
El operador .. realiza una búsqueda recursiva en todo el árbol del documento.
Es como un recorrido en profundidad que recopila cada nodo que coincide con el nombre de la clave, independientemente de dónde esté:
// Encontrar cada campo "name" en cualquier lugar del documento
$..name
// → ["Maria Chen", "Wireless Headphones", "USB-C Charging Cable", "Phone Stand Pro"]
// Encontrar cada campo "city" en cualquier lugar
$..city
// → ["Portland"]
// Encontrar cada campo llamado "id" a cualquier profundidad
$..id
// → ["ORD-9182", "CUST-441"]Observa cómo $..name encuentra tanto el nombre del cliente como todos los nombres de productos —
no le importa la profundidad. Esto es poderoso para la exploración de esquemas: cuando te entregan un
blob JSON desconocido y quieres encontrar todos los valores para una clave particular sin conocer la estructura.
Si quieres verificar el JSON primero, el Validador JSON
y el Formateador JSON son buenos puntos de partida.
Segmentación de arrays
JSONPath toma prestada la notación de slice de Python para arrays. El formato es
[start:end:step], donde cualquier parte puede omitirse.
Esto está documentado en RFC 9535 § 2.3.5:
// Primeros dos artículos de línea
$.order.lineItems[0:2]
// → [{ sku: "WH-1042", ... }, { sku: "CB-USB-C", ... }]
// Desde el índice 1 hasta el final
$.order.lineItems[1:]
// → [{ sku: "CB-USB-C", ... }, { sku: "SC-PRO-7", ... }]
// Solo el último elemento (sintaxis de slice — ampliamente compatible)
$.order.lineItems[-1:]
// → [{ sku: "SC-PRO-7", ... }]
// Cada otro elemento (paso de 2)
$.order.lineItems[0::2]
// → [{ sku: "WH-1042", ... }, { sku: "SC-PRO-7", ... }]Expresiones de filtro — Consultar por condición
Aquí es donde JSONPath realmente demuestra su valor. La sintaxis de filtro [?(...)]
te permite consultar elementos de array por sus valores de campo. El símbolo @ hace referencia al
elemento actual que se está evaluando. El
artículo original de 2007 de Stefan Goessner
introdujo esta sintaxis, y el RFC 9535 la formalizó:
// Artículos de línea por debajo de $50
$.order.lineItems[?(@.unitPrice < 50)]
// → [{ sku: "CB-USB-C", unitPrice: 12.49, ... }, { sku: "SC-PRO-7", unitPrice: 34.00, ... }]
// Solo artículos actualmente en stock
$.order.lineItems[?(@.inStock == true)]
// → [{ sku: "WH-1042", ... }, { sku: "CB-USB-C", ... }]
// Artículos donde qty es mayor que 1
$.order.lineItems[?(@.qty > 1)]
// → [{ sku: "CB-USB-C", qty: 2, ... }]
// Artículos que NO están en stock
$.order.lineItems[?(@.inStock == false)].name
// → ["Phone Stand Pro"]También puedes combinar condiciones. La mayoría de las implementaciones soportan && y ||
dentro de expresiones de filtro, permitiéndote escribir cosas como
[?(@.inStock == true && @.unitPrice < 30)].
Revisa la documentación de tu biblioteca — el comportamiento respecto al soporte de operadores fue una de las principales inconsistencias
entre las implementaciones pre-RFC que la
estandarización RFC 9535 buscó corregir.
[?(@.fieldName)]. Por ejemplo,
si algunos artículos de línea tuvieran un campo discountCode y otros no,
$.order.lineItems[?(@.discountCode)] devolvería solo los artículos con descuento.Usar JSONPath en JavaScript
Todavía no hay soporte nativo de JSONPath en JavaScript (a diferencia de XPath, que tiene
document.evaluate() en el navegador). Necesitarás una biblioteca.
Las dos más populares son jsonpath
y jsonpath-plus.
El paquete jsonpath-plus está más activamente mantenido y tiene mejor alineación con RFC 9535:
npm install jsonpath-plusimport { JSONPath } from 'jsonpath-plus';
const order = {
order: {
id: 'ORD-9182',
status: 'shipped',
customer: { name: 'Maria Chen', email: '[email protected]' },
lineItems: [
{ sku: 'WH-1042', name: 'Wireless Headphones', qty: 1, unitPrice: 89.99, inStock: true },
{ sku: 'CB-USB-C', name: 'USB-C Charging Cable', qty: 2, unitPrice: 12.49, inStock: true },
{ sku: 'SC-PRO-7', name: 'Phone Stand Pro', qty: 1, unitPrice: 34.00, inStock: false }
],
totals: { subtotal: 148.97, shipping: 5.99, tax: 11.92, total: 166.88 }
}
};
// Get all product names
const names = JSONPath({ path: '$.order.lineItems[*].name', json: order });
console.log(names);
// [ 'Wireless Headphones', 'USB-C Charging Cable', 'Phone Stand Pro' ]
// Get in-stock items under $50
const affordable = JSONPath({ path: '$.order.lineItems[?(@.inStock == true && @.unitPrice < 50)]', json: order });
console.log(affordable.map(item => item.name));
// [ 'USB-C Charging Cable' ]
// Get the order total
const total = JSONPath({ path: '$.order.totals.total', json: order });
console.log(total[0]); // 166.88Ten en cuenta que JSONPath() siempre devuelve un array — incluso cuando consultas un solo valor.
Eso es por diseño: una expresión de ruta es un selector, y un selector puede coincidir con cero, uno o muchos nodos.
Así que siempre toma result[0] cuando sabes que apuntas a un valor único.
Usar JSONPath en Python
En Python, la biblioteca
jsonpath-ng
es la opción más completa. Soporta la especificación básica más la mayoría de la sintaxis de expresión de filtro:
pip install jsonpath-ngfrom jsonpath_ng import parse
order = {
"order": {
"id": "ORD-9182",
"status": "shipped",
"customer": {"name": "Maria Chen", "email": "[email protected]"},
"lineItems": [
{"sku": "WH-1042", "name": "Wireless Headphones", "qty": 1, "unitPrice": 89.99, "inStock": True},
{"sku": "CB-USB-C", "name": "USB-C Charging Cable", "qty": 2, "unitPrice": 12.49, "inStock": True},
{"sku": "SC-PRO-7", "name": "Phone Stand Pro", "qty": 1, "unitPrice": 34.00, "inStock": False}
],
"totals": {"subtotal": 148.97, "shipping": 5.99, "tax": 11.92, "total": 166.88}
}
}
# Parse the expression once, then reuse it (more efficient)
expr = parse("$.order.lineItems[*].name")
names = [match.value for match in expr.find(order)]
print(names)
# ['Wireless Headphones', 'USB-C Charging Cable', 'Phone Stand Pro']
# Get all line items and check stock
expr2 = parse("$.order.lineItems[*]")
for item in [m.value for m in expr2.find(order)]:
status = "in stock" if item["inStock"] else "OUT OF STOCK"
print(f"{item['name']} (${item['unitPrice']}) — {status}")
# Output:
# Wireless Headphones ($89.99) — in stock
# USB-C Charging Cable ($12.49) — in stock
# Phone Stand Pro ($34.0) — OUT OF STOCKEl patrón parse() + find() es el enfoque correcto para uso en producción —
compila la expresión una vez y ejecútala contra muchos documentos, en lugar de re-parsear la cadena de ruta cada vez.
La documentación de jsonpath-ng en GitHub
tiene más detalles sobre la sintaxis de filtro extendida y el método update() para modificar nodos coincidentes.
JSONPath vs jq — ¿Cuál elegir?
A veces la gente confunde JSONPath con jq — ambos "consultan JSON", pero resuelven problemas diferentes. Aquí está el desglose práctico:
- JSONPath es un lenguaje de expresiones de ruta diseñado para ser embebido en aplicaciones. Consulta y extrae valores. Lo usas dentro de código JavaScript, Python, Java, o en archivos de configuración como los selectores de Kubernetes.
- jq es un procesador de línea de comandos completo con su propio lenguaje de programación. Puede consultar, transformar, remodelar, filtrar y calcular nuevos valores. Es la herramienta correcta para scripts de shell y manipulación de datos puntual en un terminal.
- Usa JSONPath cuando estés escribiendo código de aplicación que necesite extraer campos de JSON programáticamente — especialmente cuando la biblioteca necesita ser embebible y ligera.
- Usa jq cuando estés en la línea de comandos, escribiendo scripts de shell, depurando respuestas de API, o necesites hacer transformaciones (renombrar campos, agregar, construir nuevas estructuras) que van más allá de la simple extracción.
- La sintaxis de jq es más poderosa pero también más compleja —
jq '.order.lineItems[] | select(.inStock) | .name'vs$.order.lineItems[?(@.inStock)].namede JSONPath. Para extracción pura, JSONPath suele ser más legible.
También hay un punto intermedio: si ya trabajas en JavaScript y solo necesitas transformaciones ocasionales,
algo como _.get() de Lodash
cubre el acceso simple a rutas sin dependencias. Pero para cualquier cosa que involucre comodines, recursión,
o expresiones de filtro, vale la pena una biblioteca JSONPath adecuada.
Conclusión
JSONPath llena un espacio real entre "recorrer manualmente este objeto en un bucle for" y
"lanzar un proceso jq". Una vez que te sientes cómodo con $, ., [*],
.., y [?(filtro)], lo usarás constantemente —
especialmente al trabajar con grandes payloads de API donde solo necesitas un puñado de campos.
La estandarización RFC 9535
significa que la sintaxis ahora es estable, por lo que las expresiones que escribas hoy deberían funcionar consistentemente en
bibliotecas conformes.
Prueba tus expresiones con la herramienta JSON Path — pega tu JSON,
escribe una ruta, y ve los resultados al instante. Y si tu JSON necesita limpieza primero,
el Formateador JSON y el Validador JSON
también están disponibles.