Probador de JSONPath
Prueba consultas JSONPath (RFC 9535) contra JSON: selectores, filtros, las cinco funciones y la ruta normalizada de cada coincidencia — en tu navegador.
- Ruta
$['store']['book'][0]['title']"Sayings of the Century" - Ruta
$['store']['book'][2]['title']"Moby Dick"
Un lenguaje de consulta que por fin tiene un estándar
JSONPath es a JSON lo que XPath es a XML: un pequeño lenguaje para señalar partes de un documento. Escribes una expresión como $.store.book[0].title y selecciona los nodos que coinciden. La idea viene de una entrada de blog de Stefan Goessner de 2007, y durante diecisiete años esa entrada fue la única referencia — lo que significó que cada biblioteca implementó los huecos de forma distinta. ¿Qué selecciona un $.. a secas? ¿Se garantiza que $[1,2] vuelva en orden? ¿Cómo compara un filtro un valor que no está? Pregunta a tres bibliotecas JSONPath y podrías obtener tres respuestas.
RFC 9535, publicado en 2024, por fin lo fijó todo. Este probador apunta a ese estándar, no al folclore. Ejecuta la consulta como dice la RFC, te muestra cada coincidencia junto con su ubicación exacta y — algo importante — rechaza una consulta que es válida en algún dialecto antiguo pero no en RFC 9535, diciéndote dónde está el problema en lugar de hacer en silencio algo no estándar que otra herramienta haría de otro modo.
Segmentos y selectores
Una consulta es el identificador raíz $ seguido de una secuencia de segmentos, y cada segmento aplica uno o más selectores a los nodos que recibe. Hay cinco selectores:
- Nombre — $.store o $["store"] selecciona el valor de un miembro. La forma con punto es una abreviatura; la forma de corchetes y comillas funciona para cualquier clave, incluida una con espacios o puntuación.
- Comodín — * selecciona todos los miembros de un objeto o todos los elementos de un array.
- Índice — [0] selecciona un elemento de un array, y un índice negativo cuenta desde el final, así que [-1] es el último.
- Rebanada (slice) — [start:end:step] selecciona un rango, exactamente como en Python: [1:3] son los elementos 1 y 2, [::-1] invierte, [::2] toma uno de cada dos.
- Filtro — [?<expresión>] conserva solo los elementos o miembros para los que la expresión es verdadera (se explica más abajo).
Un corchete puede contener varios selectores a la vez: [0, 2, "title"] selecciona tres cosas en un solo segmento. Y un segmento puede ser un segmento hijo (un punto o corchete simple) o un segmento descendiente (..), que busca en el nodo y en todos sus descendientes — $..author encuentra cada author en cualquier parte del documento.
Filtros, y las cuatro formas de comparar la nada
Un selector de filtro prueba cada elemento con una expresión lógica, en la que @ se refiere al elemento actual y $ al documento entero. La expresión puede comparar valores (==, !=, <, <=, >, >=), combinar pruebas con && y || y !, y simplemente comprobar la existencia: $..book[[email protected]] conserva los libros que tienen un ISBN, porque @.isbn selecciona un nodo solo cuando la clave está presente.
La parte sutil es comparar algo que no está. Una consulta a la izquierda o a la derecha de una comparación produce un valor o nada. La RFC lo define con precisión: la nada es igual a la nada, la nada no es igual a ningún valor real, y cualquier prueba de orden (<, >) contra la nada es simplemente falsa. Así que @.price < 10 omite en silencio un elemento que no tiene price en lugar de dar error — que suele ser lo que quieres, y siempre lo que dice el estándar.
Las cinco funciones
RFC 9535 añade cinco funciones que puedes llamar dentro de un filtro:
- length() — la longitud de una cadena (contada en caracteres), un array o un objeto.
- count() — cuántos nodos selecciona una consulta, para que puedas filtrar por cardinalidad: [?count(@.chapters) > 3].
- value() — el único valor que selecciona una consulta, o nada si selecciona cero o muchos.
- match() — si una cadena coincide con una expresión regular por completo.
- search() — si una expresión regular se encuentra en cualquier parte de una cadena.
El estándar es estricto sobre cómo se usan, y este probador lo impone al analizar: length() toma exactamente un valor, así que length(@.*) — una consulta que podría seleccionar muchos nodos — es un error de sintaxis, no una consulta que se comporta mal en silencio. Del mismo modo match() devuelve verdadero o falso, así que escribir match(@.a, "x") == true se rechaza, porque un resultado lógico no es algo que se compare.
Las expresiones regulares son I-Regexp
match() y search() no usan las expresiones regulares de JavaScript; usan I-Regexp (RFC 9485), un subconjunto pequeño y portable diseñado para comportarse igual en todos los lenguajes. La mayor parte es lo que esperarías — clases de caracteres, cuantificadores, alternancia, propiedades Unicode como \p{Lu} para una letra mayúscula. La única trampa es el punto: en I-Regexp, . coincide con cualquier carácter salvo un retorno de carro o un salto de línea, lo que significa que sí coincide con los separadores de línea Unicode U+2028 y U+2029 que un punto de JavaScript excluye. Este probador compila I-Regexp con fidelidad, de modo que un patrón se comporta aquí como lo evaluaría un servidor conforme.
La diferencia entre las dos funciones es solo el anclaje: match exige que toda la cadena coincida, mientras que search busca el patrón en cualquier parte de ella. match(@, "a.*") acepta "abc"; search(@, "b") acepta cualquier cadena que contenga una b.
Rutas normalizadas, y ejecución en el navegador
Para cada coincidencia, este probador muestra una Normalized Path — la ubicación canónica que define la RFC, escrita con la forma de corchetes y comillas: $['store']['book'][0]['author']. A diferencia de la consulta, que puede seleccionar muchos nodos, una ruta normalizada apunta a exactamente uno, usando solo selectores de nombre e índice y un estilo de comillas fijo. Es la respuesta a "¿de dónde vino esta coincidencia?", y es lo que te permite convertir un resultado de comodín de nuevo en un conjunto de ubicaciones concretas. La mayoría de los probadores muestran los valores y te dejan deducir las rutas; este muestra ambos.
Todo el motor se ejecuta en tu navegador — el JSON se analiza y la consulta se evalúa en tu propio dispositivo, y nada de lo que pegues se sube, almacena ni registra. Está validado contra la suite oficial de pruebas de conformidad de JSONPath, así que sus respuestas coinciden con el estándar y no con la interpretación de una sola biblioteca.
Preguntas frecuentes
- ¿Qué dialecto de JSONPath usa esto?
- RFC 9535, el estándar del IETF de 2024, y está validado contra la suite oficial de pruebas de conformidad de JSONPath. Deliberadamente no acepta las viejas convenciones de Goessner donde divergen de la RFC; una consulta así se rechaza con la posición del problema para que puedas corregirla.
- ¿Por qué se rechazó mi consulta si funciona en otra herramienta?
- Porque esa herramienta sigue las convenciones de Goessner previas al estándar, que difieren de RFC 9535 en varios puntos — un $.. a secas, expresiones de script como [(@.length-1)], índices con cero inicial y nombres sin comillas con caracteres especiales son todos no estándar. La RFC los reemplazó con equivalentes bien definidos, y este probador se atiene a la RFC.
- ¿Qué es una ruta normalizada?
- La ubicación canónica de un solo nodo, escrita en la forma de corchetes y comillas que define la RFC, p. ej. $['store']['book'][0]['author']. Una consulta puede coincidir con muchos nodos; cada coincidencia tiene exactamente una ruta normalizada, por eso el probador la muestra junto a cada valor.
- ¿Cómo trata un filtro un valor ausente?
- Como nada, con reglas que la RFC fija: la nada es igual a la nada, la nada no es igual a ningún valor real, y cualquier comparación de orden (<, >) que implique la nada es falsa. Así que @.price < 10 simplemente omite un elemento sin price en lugar de lanzar un error.
- ¿match() y search() usan las expresiones regulares de JavaScript?
- No. Usan I-Regexp (RFC 9485), un subconjunto portable. La principal diferencia práctica es el punto, que coincide con todo salvo retorno de carro y salto de línea — incluidos U+2028 y U+2029, que un punto de JavaScript excluye. Este probador compila I-Regexp con fidelidad para que los resultados coincidan con una implementación conforme.
- ¿Cuál es la diferencia entre match y search?
- El anclaje. match() exige que toda la cadena coincida con el patrón, como si estuviera entre anclas; search() tiene éxito si el patrón se encuentra en cualquier parte de la cadena. Todo lo demás en ambas es idéntico.
- ¿Se envía mi JSON a un servidor?
- No. El documento se analiza y la consulta se evalúa por completo en tu navegador, y nada de lo que pegues se sube ni se registra. Es seguro probar contra una respuesta de API real o un archivo de configuración.