Un DSL Java para leer documentos JSON.
Jayway JsonPath es un puerto Java de la implementación de Stefan Goessner JsonPath.
JsonPath está disponible en el repositorio central de Maven. Los usuarios de Maven agregan esto a su POM.
< dependency >
< groupId >com.jayway.jsonpath</ groupId >
< artifactId >json-path</ artifactId >
< version >2.9.0</ version >
</ dependency >
Si necesita ayuda, haga preguntas en Stack Overflow. Etiquete la pregunta 'jsonpath' y 'java'.
Las expresiones JsonPath siempre hacen referencia a una estructura JSON de la misma manera que las expresiones XPath se usan en combinación con un documento XML. El "objeto miembro raíz" en JsonPath siempre se denomina $
independientemente de si es un objeto o una matriz.
Las expresiones JsonPath pueden usar la notación de puntos.
$.store.book[0].title
o la notación entre corchetes
$['store']['book'][0]['title']
Operador | Descripción |
---|---|
$ | El elemento raíz a consultar. Esto inicia todas las expresiones de ruta. |
@ | El nodo actual que está siendo procesado por un predicado de filtro. |
* | Comodín. Disponible en cualquier lugar donde se requiera un nombre o un número. |
.. | Escaneo profundo. Disponible en cualquier lugar donde se requiera un nombre. |
.<name> | Niño anotado por puntos |
['<name>' (, '<name>')] | Niño o niños anotados entre corchetes |
[<number> (, <number>)] | Índice o índices de matriz |
[start:end] | Operador de corte de matriz |
[?(<expression>)] | Filtrar expresión. La expresión debe evaluarse como un valor booleano. |
Las funciones se pueden invocar al final de una ruta: la entrada a una función es la salida de la expresión de la ruta. La salida de la función está dictada por la función misma.
Función | Descripción | Tipo de salida |
---|---|---|
min() | Proporciona el valor mínimo de una matriz de números. | Doble |
max() | Proporciona el valor máximo de una matriz de números. | Doble |
avg() | Proporciona el valor promedio de una matriz de números. | Doble |
stddev() | Proporciona el valor de desviación estándar de una matriz de números. | Doble |
length() | Proporciona la longitud de una matriz. | Entero |
sum() | Proporciona el valor suma de una matriz de números. | Doble |
keys() | Proporciona las claves de propiedad (una alternativa para la tilde del terminal ~ ) | Set<E> |
concat(X) | Proporciona una versión concatinada de la salida de la ruta con un nuevo elemento. | como entrada |
append(X) | agregar un elemento a la matriz de salida de la ruta json | como entrada |
first() | Proporciona el primer elemento de una matriz. | Depende de la matriz |
last() | Proporciona el último elemento de una matriz. | Depende de la matriz |
index(X) | Proporciona el elemento de una matriz de índice: X, si X es negativo, se toma desde atrás | Depende de la matriz |
Los filtros son expresiones lógicas que se utilizan para filtrar matrices. Un filtro típico sería [?(@.age > 18)]
donde @
representa el elemento actual que se está procesando. Se pueden crear filtros más complejos con los operadores lógicos &&
y ||
. Los literales de cadena deben estar entre comillas simples o dobles ( [?(@.color == 'blue')]
o [?(@.color == "blue")]
] .
Operador | Descripción |
---|---|
== | izquierda es igual a derecha (tenga en cuenta que 1 no es igual a '1') |
!= | izquierda no es igual a derecha |
< | la izquierda es menos que la derecha |
<= | la izquierda es menor o igual que la derecha |
> | la izquierda es mayor que la derecha |
>= | la izquierda es mayor o igual que la derecha |
=~ | izquierda coincide con la expresión regular [?(@.name =~ /foo.*?/i)] |
in | la izquierda existe en la derecha [?(@.size en ['S', 'M'])] |
nin | la izquierda no existe en la derecha |
subsetof | left es un subconjunto de right [?(@.sizes subsetof ['S', 'M', 'L'])] |
anyof | la izquierda tiene una intersección con la derecha [?(@.sizes anyof ['M', 'L'])] |
noneof | la izquierda no tiene intersección con la derecha [?(@.sizes noneof ['M', 'L'])] |
size | El tamaño de la izquierda (matriz o cadena) debe coincidir con la derecha. |
empty | left (matriz o cadena) debe estar vacía |
Dado el json
{
"store" : {
"book" : [
{
"category" : "reference" ,
"author" : "Nigel Rees" ,
"title" : "Sayings of the Century" ,
"price" : 8.95
} ,
{
"category" : "fiction" ,
"author" : "Evelyn Waugh" ,
"title" : "Sword of Honour" ,
"price" : 12.99
} ,
{
"category" : "fiction" ,
"author" : "Herman Melville" ,
"title" : "Moby Dick" ,
"isbn" : "0-553-21311-3" ,
"price" : 8.99
} ,
{
"category" : "fiction" ,
"author" : "J. R. R. Tolkien" ,
"title" : "The Lord of the Rings" ,
"isbn" : "0-395-19395-8" ,
"price" : 22.99
}
] ,
"bicycle" : {
"color" : "red" ,
"price" : 19.95
}
} ,
"expensive" : 10
}
JsonPath | Resultado |
---|---|
$.store.book[*].author | Los autores de todos los libros. |
$..author | Todos los autores |
$.store.* | Todas las cosas, tanto libros como bicicletas. |
$.store..price | El precio de todo |
$..book[2] | el tercer libro |
$..book[-2] | El penúltimo libro. |
$..book[0,1] | Los dos primeros libros |
$..book[:2] | Todos los libros desde el índice 0 (inclusive) hasta el índice 2 (exclusivo) |
$..book[1:2] | Todos los libros desde el índice 1 (inclusive) hasta el índice 2 (exclusivo) |
$..book[-2:] | dos ultimos libros |
$..book[2:] | Todos los libros desde el índice 2 (inclusive) hasta el último |
$..book[?(@.isbn)] | Todos los libros con un número ISBN |
$.store.book[?(@.price < 10)] | Todos los libros en tienda por menos de 10 |
$..book[?(@.price <= $['expensive'])] | Todos los libros en tienda que no son "caros" |
$..book[?(@.author =~ /.*REES/i)] | Todos los libros que coinciden con expresiones regulares (ignorar mayúsculas y minúsculas) |
$..* | Dame todo |
$..book.length() | El número de libros |
La forma más sencilla y directa de utilizar JsonPath es a través de la API de lectura estática.
String json = "..." ;
List < String > authors = JsonPath . read ( json , "$.store.book[*].author" );
Si sólo quieres leer una vez, está bien. En caso de que también necesite leer otra ruta, este no es el camino a seguir ya que el documento se analizará cada vez que llame a JsonPath.read(...). Para evitar el problema, primero puede analizar el json.
String json = "..." ;
Object document = Configuration . defaultConfiguration (). jsonProvider (). parse ( json );
String author0 = JsonPath . read ( document , "$.store.book[0].author" );
String author1 = JsonPath . read ( document , "$.store.book[1].author" );
JsonPath también proporciona una API fluida. Este también es el más flexible.
String json = "..." ;
ReadContext ctx = JsonPath . parse ( json );
List < String > authorsOfBooksWithISBN = ctx . read ( "$.store.book[?(@.isbn)].author" );
List < Map < String , Object >> expensiveBooks = JsonPath
. using ( configuration )
. parse ( json )
. read ( "$.store.book[?(@.price > 10)]" , List . class );
Al usar JsonPath en Java, es importante saber qué tipo espera en su resultado. JsonPath intentará automáticamente convertir el resultado al tipo esperado por el invocador.
//Will throw an java.lang.ClassCastException
List < String > list = JsonPath . parse ( json ). read ( "$.store.book[0].author" );
//Works fine
String author = JsonPath . parse ( json ). read ( "$.store.book[0].author" );
Al evaluar un camino es necesario comprender el concepto de cuándo un camino es definite
. Un camino es indefinite
si contiene:
..
- un operador de escaneo profundo?(<expression>)
- una expresión[<number>, <number> (, <number>)]
- múltiples índices de matriz Las rutas Indefinite
siempre devuelven una lista (representada por el JsonProvider actual).
De forma predeterminada, MappingProvider SPI proporciona un asignador de objetos simple. Esto le permite especificar el tipo de devolución que desea y MappingProvider intentará realizar la asignación. En el siguiente ejemplo se demuestra el mapeo entre Long
y Date
.
String json = "{ " date_as_long " : 1411455611975}" ;
Date date = JsonPath . parse ( json ). read ( "$['date_as_long']" , Date . class );
Si configura JsonPath para usar JacksonMappingProvider
, GsonMappingProvider
o JakartaJsonProvider
, incluso puede asignar su salida de JsonPath directamente a POJO.
Book book = JsonPath . parse ( json ). read ( "$.store.book[0]" , Book . class );
Para obtener información completa sobre los tipos de genéricos, utilice TypeRef.
TypeRef < List < String >> typeRef = new TypeRef < List < String >>() {};
List < String > titles = JsonPath . parse ( JSON_DOCUMENT ). read ( "$.store.book[*].title" , typeRef );
Hay tres formas diferentes de crear predicados de filtro en JsonPath.
Los predicados en línea son los definidos en la ruta.
List < Map < String , Object >> books = JsonPath . parse ( json )
. read ( "$.store.book[?(@.price < 10)]" );
Puedes usar &&
y ||
para combinar múltiples predicados [?(@.price < 10 && @.category == 'fiction')]
, [?(@.category == 'reference' || @.price > 10)]
.
¡Puedes usar !
para negar un predicado [?(!(@.price < 10 && @.category == 'fiction'))]
.
Los predicados se pueden crear utilizando la API de filtro como se muestra a continuación:
import static com . jayway . jsonpath . JsonPath . parse ;
import static com . jayway . jsonpath . Criteria . where ;
import static com . jayway . jsonpath . Filter . filter ;
...
...
Filter cheapFictionFilter = filter (
where ( "category" ). is ( "fiction" ). and ( "price" ). lte ( 10D )
);
List < Map < String , Object >> books =
parse ( json ). read ( "$.store.book[?]" , cheapFictionFilter );
¿Notas el marcador de posición ?
para el filtro en el camino. Cuando se proporcionan varios filtros, se aplican en orden donde la cantidad de marcadores de posición debe coincidir con la cantidad de filtros proporcionados. Puede especificar varios marcadores de posición de predicado en una operación de filtro [?, ?]
; ambos predicados deben coincidir.
Los filtros también se pueden combinar con 'OR' y 'AND'
Filter fooOrBar = filter (
where ( "foo" ). exists ( true )). or ( where ( "bar" ). exists ( true )
);
Filter fooAndBar = filter (
where ( "foo" ). exists ( true )). and ( where ( "bar" ). exists ( true )
);
La tercera opción es implementar sus propios predicados.
Predicate booksWithISBN = new Predicate () {
@ Override
public boolean apply ( PredicateContext ctx ) {
return ctx . item ( Map . class ). containsKey ( "isbn" );
}
};
List < Map < String , Object >> books =
reader . read ( "$.store.book[?].isbn" , List . class , booksWithISBN );
En la implementación de Goessner, un JsonPath puede devolver Path
o Value
. Value
es el predeterminado y lo que devuelven todos los ejemplos anteriores. Si prefiere tener la ruta de los elementos a los que llega nuestra consulta, esto se puede lograr con una opción.
Configuration conf = Configuration . builder ()
. options ( Option . AS_PATH_LIST ). build ();
List < String > pathList = using ( conf ). parse ( json ). read ( "$..author" );
assertThat ( pathList ). containsExactly (
"$['store']['book'][0]['author']" ,
"$['store']['book'][1]['author']" ,
"$['store']['book'][2]['author']" ,
"$['store']['book'][3]['author']" );
La biblioteca ofrece la posibilidad de establecer un valor.
String newJson = JsonPath . parse ( json ). set ( "$['store']['book'][0]['author']" , "Paul" ). jsonString ();
Al crear su configuración, hay algunas opciones que pueden alterar el comportamiento predeterminado.
DEFAULT_PATH_LEAF_TO_NULL
Esta opción hace que JsonPath devuelva un valor nulo para las hojas faltantes. Considere el siguiente json
[
{
"name" : "john" ,
"gender" : "male"
} ,
{
"name" : "ben"
}
]
Configuration conf = Configuration . defaultConfiguration ();
//Works fine
String gender0 = JsonPath . using ( conf ). parse ( json ). read ( "$[0]['gender']" );
//PathNotFoundException thrown
String gender1 = JsonPath . using ( conf ). parse ( json ). read ( "$[1]['gender']" );
Configuration conf2 = conf . addOptions ( Option . DEFAULT_PATH_LEAF_TO_NULL );
//Works fine
String gender0 = JsonPath . using ( conf2 ). parse ( json ). read ( "$[0]['gender']" );
//Works fine (null is returned)
String gender1 = JsonPath . using ( conf2 ). parse ( json ). read ( "$[1]['gender']" );
ALWAYS_RETURN_LIST
Esta opción configura JsonPath para devolver una lista incluso cuando la ruta es definite
.
Configuration conf = Configuration . defaultConfiguration ();
//ClassCastException thrown
List < String > genders0 = JsonPath . using ( conf ). parse ( json ). read ( "$[0]['gender']" );
Configuration conf2 = conf . addOptions ( Option . ALWAYS_RETURN_LIST );
//Works fine
List < String > genders0 = JsonPath . using ( conf2 ). parse ( json ). read ( "$[0]['gender']" );
SUPRIMIR_EXCEPCIONES
Esta opción garantiza que no se propaguen excepciones desde la evaluación de ruta. Sigue estas simples reglas:
ALWAYS_RETURN_LIST
está presente, se devolverá una lista vacíaALWAYS_RETURN_LIST
NO está presente se devuelve nulo REQUIRE_PROPERTIES
Esta opción configura JsonPath para requerir propiedades definidas en la ruta cuando se evalúa una ruta indefinite
.
Configuration conf = Configuration . defaultConfiguration ();
//Works fine
List < String > genders = JsonPath . using ( conf ). parse ( json ). read ( "$[*]['gender']" );
Configuration conf2 = conf . addOptions ( Option . REQUIRE_PROPERTIES );
//PathNotFoundException thrown
List < String > genders = JsonPath . using ( conf2 ). parse ( json ). read ( "$[*]['gender']" );
JsonPath se envía con cinco JsonProviders diferentes:
Cambiar los valores predeterminados de configuración como se muestra solo debe realizarse cuando se inicializa la aplicación. Se desaconseja encarecidamente realizar cambios durante el tiempo de ejecución, especialmente en aplicaciones de subprocesos múltiples.
Configuration . setDefaults ( new Configuration . Defaults () {
private final JsonProvider jsonProvider = new JacksonJsonProvider ();
private final MappingProvider mappingProvider = new JacksonMappingProvider ();
@ Override
public JsonProvider jsonProvider () {
return jsonProvider ;
}
@ Override
public MappingProvider mappingProvider () {
return mappingProvider ;
}
@ Override
public Set < Option > options () {
return EnumSet . noneOf ( Option . class );
}
});
Tenga en cuenta que JacksonJsonProvider requiere com.fasterxml.jackson.core:jackson-databind:2.4.5
y GsonJsonProvider requiere com.google.code.gson:gson:2.3.1
en su classpath.
Tanto los proveedores de Jakarta EE 9 JSON-P (JSR-342) como JSON-B (JSR-367) esperan al menos Java 8 y requieren implementaciones de API JSON compatibles (como Eclipse Glassfish y Eclipse Yasson) en la ruta de clase del tiempo de ejecución de la aplicación; dichas implementaciones también pueden ser proporcionadas por el contenedor de aplicaciones Java EE. Tenga en cuenta también que Apache Johnzon aún no es compatible con classpath con la especificación Jakarta EE 9, y si se elige el proveedor de mapeo JSON-B, también se debe configurar y utilizar el proveedor JSON-P.
Una peculiaridad de las especificaciones de Jakarta EE 9 para el procesamiento y enlace de datos (mapeo) JSON es la inmutabilidad de las matrices y objetos Json tan pronto como se analizan o escriben por completo. Para respetar la especificación API, pero permitir que JsonPath modifique documentos Json mediante operaciones de agregar, configurar/poner, reemplazar y eliminar, JakartaJsonProvider
debe iniciarse con un argumento true
opcional:
JsonProvider jsonProvider = new JakartaJsonProvider(true)
(habilita objetos y matrices Json mutables)JsonProvider jsonProvider = new JakartaJsonProvider()
(predeterminado, cumplimiento estricto de la API JSON-P)Todas las operaciones de búsqueda y lectura con JsonPath son compatibles independientemente del modo de inicialización. El modo predeterminado también necesita menos memoria y tiene más rendimiento.
En JsonPath 2.1.0 se introdujo un nuevo Cache SPI. Esto permite a los consumidores de API configurar el almacenamiento en caché de rutas de una manera que se adapte a sus necesidades. La caché debe configurarse antes de acceder a ella por primera vez o se generará una excepción JsonPathException. JsonPath viene con dos implementaciones de caché
com.jayway.jsonpath.spi.cache.LRUCache
(predeterminado, seguro para subprocesos)com.jayway.jsonpath.spi.cache.NOOPCache
(sin caché)Si desea implementar su propio caché, la API es simple.
CacheProvider . setCache ( new Cache () {
//Not thread safe simple cache
private Map < String , JsonPath > map = new HashMap < String , JsonPath >();
@ Override
public JsonPath get ( String key ) {
return map . get ( key );
}
@ Override
public void put ( String key , JsonPath jsonPath ) {
map . put ( key , jsonPath );
}
});