Eine Java-DSL zum Lesen von JSON-Dokumenten.
Jayway JsonPath ist eine Java-Portierung der JsonPath-Implementierung von Stefan Goessner.
JsonPath ist im Central Maven Repository verfügbar. Maven-Benutzer fügen dies Ihrem POM hinzu.
< dependency >
< groupId >com.jayway.jsonpath</ groupId >
< artifactId >json-path</ artifactId >
< version >2.9.0</ version >
</ dependency >
Wenn Sie Hilfe benötigen, stellen Sie Fragen bei Stack Overflow. Markieren Sie die Frage mit „jsonpath“ und „java“.
JsonPath-Ausdrücke verweisen immer auf eine JSON-Struktur, genauso wie XPath-Ausdrücke in Kombination mit einem XML-Dokument verwendet werden. Das „Stammmitgliedsobjekt“ in JsonPath wird immer als $
bezeichnet, unabhängig davon, ob es sich um ein Objekt oder ein Array handelt.
JsonPath-Ausdrücke können die Punktnotation verwenden
$.store.book[0].title
oder die Klammernotation
$['store']['book'][0]['title']
Operator | Beschreibung |
---|---|
$ | Das abzufragende Stammelement. Dadurch werden alle Pfadausdrücke gestartet. |
@ | Der aktuelle Knoten, der von einem Filterprädikat verarbeitet wird. |
* | Platzhalter. Überall dort verfügbar, wo ein Name oder eine Nummer erforderlich ist. |
.. | Tiefenscan. Überall dort verfügbar, wo ein Name erforderlich ist. |
.<name> | Punktnotiertes Kind |
['<name>' (, '<name>')] | In Klammern notiertes Kind oder Kinder |
[<number> (, <number>)] | Array-Index oder -Indizes |
[start:end] | Array-Slice-Operator |
[?(<expression>)] | Filterausdruck. Der Ausdruck muss einen booleschen Wert ergeben. |
Funktionen können am Ende eines Pfads aufgerufen werden – die Eingabe einer Funktion ist die Ausgabe des Pfadausdrucks. Die Funktionsausgabe wird durch die Funktion selbst bestimmt.
Funktion | Beschreibung | Ausgabetyp |
---|---|---|
min() | Gibt den Mindestwert eines Zahlenarrays an | Doppelt |
max() | Gibt den Maximalwert eines Zahlenarrays an | Doppelt |
avg() | Gibt den Durchschnittswert eines Zahlenarrays an | Doppelt |
stddev() | Gibt den Standardabweichungswert eines Zahlenarrays an | Doppelt |
length() | Gibt die Länge eines Arrays an | Ganze Zahl |
sum() | Stellt den Summenwert eines Zahlenarrays bereit | Doppelt |
keys() | Stellt die Eigenschaftsschlüssel bereit (Eine Alternative für Terminal-Tilde ~ ) | Set<E> |
concat(X) | Stellt eine verknüpfte Version der Pfadausgabe mit einem neuen Element bereit | wie Eingabe |
append(X) | Fügen Sie dem JSON-Pfad-Ausgabearray ein Element hinzu | wie Eingabe |
first() | Stellt das erste Element eines Arrays bereit | Hängt vom Array ab |
last() | Stellt das letzte Element eines Arrays bereit | Hängt vom Array ab |
index(X) | Stellt das Element eines Index-Arrays bereit: X, wenn X negativ ist, wird von hinten übernommen | Hängt vom Array ab |
Filter sind logische Ausdrücke, die zum Filtern von Arrays verwendet werden. Ein typischer Filter wäre [?(@.age > 18)]
wobei @
das aktuell verarbeitete Element darstellt. Komplexere Filter können mit den logischen Operatoren &&
und ||
erstellt werden . Zeichenfolgenliterale müssen in einfache oder doppelte Anführungszeichen gesetzt werden ( [?(@.color == 'blue')]
oder [?(@.color == "blue")]
).
Operator | Beschreibung |
---|---|
== | links ist gleich rechts (beachten Sie, dass 1 nicht gleich '1' ist) |
!= | links ist nicht gleich rechts |
< | links ist kleiner als rechts |
<= | links ist kleiner oder gleich rechts |
> | links ist größer als rechts |
>= | links ist größer oder gleich rechts |
=~ | left entspricht regulärem Ausdruck [?(@.name =~ /foo.*?/i)] |
in | left existiert in right [?(@.size in ['S', 'M'])] |
nin | left existiert nicht in right |
subsetof | left ist eine Teilmenge von right [?(@.sizes subsetof ['S', 'M', 'L'])] |
anyof | left hat einen Schnittpunkt mit right [?(@.sizes anyof ['M', 'L'])] |
noneof | left hat keinen Schnittpunkt mit right [?(@.sizes noneof ['M', 'L'])] |
size | Die Größe von links (Array oder String) sollte mit rechts übereinstimmen |
empty | left (Array oder String) sollte leer sein |
Angesichts des 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 | Ergebnis |
---|---|
$.store.book[*].author | Die Autoren aller Bücher |
$..author | Alle Autoren |
$.store.* | Alle Dinge, sowohl Bücher als auch Fahrräder |
$.store..price | Der Preis für alles |
$..book[2] | Das dritte Buch |
$..book[-2] | Das vorletzte Buch |
$..book[0,1] | Die ersten beiden Bücher |
$..book[:2] | Alle Bücher von Index 0 (inklusive) bis Index 2 (exklusiv) |
$..book[1:2] | Alle Bücher von Index 1 (inklusive) bis Index 2 (exklusiv) |
$..book[-2:] | Die letzten beiden Bücher |
$..book[2:] | Alle Bücher von Index 2 (einschließlich) bis zuletzt |
$..book[?(@.isbn)] | Alle Bücher mit einer ISBN-Nummer |
$.store.book[?(@.price < 10)] | Alle Bücher im Laden günstiger als 10 |
$..book[?(@.price <= $['expensive'])] | Alle Bücher im Shop, die nicht „teuer“ sind |
$..book[?(@.author =~ /.*REES/i)] | Alle Bücher mit regulärem Ausdruck (Groß- und Kleinschreibung ignorieren) |
$..* | Gib mir alles |
$..book.length() | Die Anzahl der Bücher |
Die einfachste und unkomplizierteste Möglichkeit, JsonPath zu verwenden, ist die statische Lese-API.
String json = "..." ;
List < String > authors = JsonPath . read ( json , "$.store.book[*].author" );
Wenn Sie nur einmal lesen möchten, ist das in Ordnung. Falls Sie auch einen anderen Pfad lesen müssen, ist dies nicht der richtige Weg, da das Dokument jedes Mal analysiert wird, wenn Sie JsonPath.read(...) aufrufen. Um das Problem zu vermeiden, können Sie zuerst den JSON analysieren.
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 bietet auch eine fließende API. Dies ist auch die flexibelste Variante.
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 );
Wenn Sie JsonPath in Java verwenden, ist es wichtig zu wissen, welchen Typ Sie in Ihrem Ergebnis erwarten. JsonPath versucht automatisch, das Ergebnis in den vom Aufrufer erwarteten Typ umzuwandeln.
//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" );
Wenn Sie einen Pfad bewerten, müssen Sie das Konzept verstehen, wann ein Pfad definite
ist. Ein Pfad ist indefinite
, wenn er Folgendes enthält:
..
- ein Deep-Scan-Operator?(<expression>)
– ein Ausdruck[<number>, <number> (, <number>)]
– mehrere Array-Indizes Indefinite
Pfade geben immer eine Liste zurück (wie durch den aktuellen JsonProvider dargestellt).
Standardmäßig wird von der MappingProvider-SPI ein einfacher Objekt-Mapper bereitgestellt. Dadurch können Sie den gewünschten Rückgabetyp angeben und der MappingProvider versucht, die Zuordnung durchzuführen. Im folgenden Beispiel wird die Zuordnung zwischen Long
und Date
demonstriert.
String json = "{ " date_as_long " : 1411455611975}" ;
Date date = JsonPath . parse ( json ). read ( "$['date_as_long']" , Date . class );
Wenn Sie JsonPath für die Verwendung JacksonMappingProvider
, GsonMappingProvider
oder JakartaJsonProvider
konfigurieren, können Sie Ihre JsonPath-Ausgabe sogar direkt POJOs zuordnen.
Book book = JsonPath . parse ( json ). read ( "$.store.book[0]" , Book . class );
Um vollständige generische Typinformationen zu erhalten, verwenden Sie TypeRef.
TypeRef < List < String >> typeRef = new TypeRef < List < String >>() {};
List < String > titles = JsonPath . parse ( JSON_DOCUMENT ). read ( "$.store.book[*].title" , typeRef );
Es gibt drei verschiedene Möglichkeiten, Filterprädikate in JsonPath zu erstellen.
Inline-Prädikate sind diejenigen, die im Pfad definiert sind.
List < Map < String , Object >> books = JsonPath . parse ( json )
. read ( "$.store.book[?(@.price < 10)]" );
Sie können &&
und ||
verwenden um mehrere Prädikate zu kombinieren [?(@.price < 10 && @.category == 'fiction')]
, [?(@.category == 'reference' || @.price > 10)]
.
Sie können verwenden !
um ein Prädikat zu negieren [?(!(@.price < 10 && @.category == 'fiction'))]
.
Prädikate können mit der Filter-API wie unten gezeigt erstellt werden:
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 );
Beachten Sie den Platzhalter ?
für den Filter im Pfad. Wenn mehrere Filter bereitgestellt werden, werden diese in der Reihenfolge angewendet, in der die Anzahl der Platzhalter mit der Anzahl der bereitgestellten Filter übereinstimmen muss. Sie können in einem Filtervorgang mehrere Prädikatplatzhalter angeben [?, ?]
, beide Prädikate müssen übereinstimmen.
Filter können auch mit „OR“ und „AND“ kombiniert werden.
Filter fooOrBar = filter (
where ( "foo" ). exists ( true )). or ( where ( "bar" ). exists ( true )
);
Filter fooAndBar = filter (
where ( "foo" ). exists ( true )). and ( where ( "bar" ). exists ( true )
);
Die dritte Möglichkeit besteht darin, eigene Prädikate zu implementieren
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 );
In der Goessner-Implementierung kann ein JsonPath entweder Path
oder Value
zurückgeben. Value
ist der Standardwert und das, was alle oben genannten Beispiele zurückgeben. Wenn Sie lieber den Pfad der Elemente haben, auf die unsere Abfrage trifft, können Sie dies mit einer Option erreichen.
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']" );
Die Bibliothek bietet die Möglichkeit, einen Wert festzulegen.
String newJson = JsonPath . parse ( json ). set ( "$['store']['book'][0]['author']" , "Paul" ). jsonString ();
Beim Erstellen Ihrer Konfiguration gibt es einige Optionsflags, die das Standardverhalten ändern können.
DEFAULT_PATH_LEAF_TO_NULL
Diese Option sorgt dafür, dass JsonPath für fehlende Blätter null zurückgibt. Betrachten Sie den folgenden 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
Diese Option konfiguriert JsonPath so, dass eine Liste zurückgegeben wird, selbst wenn der Pfad definite
ist.
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']" );
SUPPRESS_EXCEPTIONS
Diese Option stellt sicher, dass bei der Pfadauswertung keine Ausnahmen weitergegeben werden. Es folgt diesen einfachen Regeln:
ALWAYS_RETURN_LIST
vorhanden ist, wird eine leere Liste zurückgegebenALWAYS_RETURN_LIST
NICHT vorhanden ist, wird null zurückgegeben REQUIRE_PROPERTIES
Diese Option konfiguriert JsonPath so, dass im Pfad definierte Eigenschaften erforderlich sind, wenn ein indefinite
Pfad ausgewertet wird.
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 wird mit fünf verschiedenen JsonProvidern ausgeliefert:
Das Ändern der Konfigurationsstandardwerte wie gezeigt sollte nur erfolgen, wenn Ihre Anwendung initialisiert wird. Von Änderungen während der Laufzeit wird dringend abgeraten, insbesondere bei Multithread-Anwendungen.
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 );
}
});
Beachten Sie, dass der JacksonJsonProvider com.fasterxml.jackson.core:jackson-databind:2.4.5
und der GsonJsonProvider com.google.code.gson:gson:2.3.1
in Ihrem Klassenpfad erfordert.
Beide Anbieter von Jakarta EE 9 JSON-P (JSR-342) und JSON-B (JSR-367) erwarten mindestens Java 8 und erfordern kompatible JSON-API-Implementierungen (wie Eclipse Glassfish und Eclipse Yasson) im Klassenpfad der Anwendungslaufzeit; Solche Implementierungen können auch vom Java EE-Anwendungscontainer bereitgestellt werden. Bitte beachten Sie auch, dass Apache Johnzon noch nicht mit der Jakarta EE 9-Spezifikation klassenpfadkompatibel ist und wenn der JSON-B-Zuordnungsanbieter ausgewählt wird, muss auch der JSON-P-Anbieter konfiguriert und verwendet werden.
Eine Besonderheit der Jakarta EE 9-Spezifikationen für die JSON-Verarbeitung und Datenbindung (Zuordnung) ist die Unveränderlichkeit von Json-Arrays und -Objekten, sobald sie vollständig analysiert oder beschrieben werden. Um die API-Spezifikation zu respektieren, aber JsonPath das Ändern von Json-Dokumenten durch Add-, Set/Put-, Replacement- und Delete-Vorgänge zu ermöglichen, muss JakartaJsonProvider
mit dem optionalen true
Argument initialisiert werden:
JsonProvider jsonProvider = new JakartaJsonProvider(true)
(veränderbare Json-Arrays und -Objekte aktivieren)JsonProvider jsonProvider = new JakartaJsonProvider()
(Standard, strikte JSON-P-API-Konformität)Alle Such- und Lesevorgänge mit JsonPath werden unabhängig vom Initialisierungsmodus unterstützt. Der Standardmodus benötigt außerdem weniger Speicher und ist leistungsfähiger.
In JsonPath 2.1.0 wurde ein neuer Cache-SPI eingeführt. Dadurch können API-Konsumenten das Pfad-Caching so konfigurieren, dass es ihren Anforderungen entspricht. Der Cache muss vor dem ersten Zugriff konfiguriert werden, sonst wird eine JsonPathException ausgelöst. JsonPath wird mit zwei Cache-Implementierungen geliefert
com.jayway.jsonpath.spi.cache.LRUCache
(Standard, Thread-sicher)com.jayway.jsonpath.spi.cache.NOOPCache
(kein Cache)Wenn Sie Ihren eigenen Cache implementieren möchten, ist die API einfach.
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 );
}
});