Uvod u JsonPath

1. Pregled

Jedna od prednosti XML-a je dostupnost obrade - uključujući XPath - koja je definirana kao W3C standard. Za JSON se pojavio sličan alat nazvan JSONPath.

Ovaj će članak dati članak uvod u Jayway JsonPath, Java implementacija JSONPath specifikacije. Opisuje postavljanje, sintaksu, uobičajene API-je i demonstraciju slučajeva korištenja.

2. Postavljanje

Da bismo koristili JsonPath, jednostavno moramo uključiti ovisnost u Maven pom:

 com.jayway.jsonpath json-path 2.4.0 

3. Sintaksa

Sljedeća JSON struktura koristit će se u ovom odjeljku za demonstraciju sintakse i API-ja JsonPath:

{"tool": {"jsonpath": {"creator": {"name": "Jayway Inc.", "location": ["Malmo", "San Francisco", "Helsingborg"]}}}, "knjiga ": [{" title ":" Beginning JSON "," price ": 49.99}, {" title ":" JSON na poslu "," price ": 29.99}]}

3.1. Notacija

JsonPath koristi posebnu notaciju za predstavljanje čvorova i njihovih veza sa susjednim čvorovima u JsonPath stazi. Postoje dva stila zapisa, naime točka i zagrada.

Oba se sljedeća puta odnose na isti čvor iz gornjeg JSON dokumenta, koji je treći element u mjesto polje od stvoritelj čvor, koji je podređen sustavu jsonpath objekt koji pripada alat ispod korijenskog čvora.

S oznakom točke:

$ .tool.jsonpath.creator.location [2]

S oznakom zagrade:

$ ['alat'] ['jsonpath'] ['kreator'] ['lokacija'] [2]

Znak dolara ($) predstavlja objekt člana korijena.

3.2. Operateri

U JsonPathu imamo nekoliko korisnih operatora:

Korijenski čvor ($): Ovaj simbol označava korijenski član JSON strukture bez obzira na to je li riječ o objektu ili nizu. Primjeri njegove uporabe uključeni su u prethodni pododjeljak.

Trenutni čvor (@): Predstavlja čvor koji se obrađuje, uglavnom se koristi kao dio ulaznih izraza za predikate. Pretpostavimo da imamo posla knjiga niz u gornjem JSON dokumentu, izraz knjiga [? (@. cijena == 49,99)] odnosi se na prvu knjiga u tom nizu.

Zamjenski znak (*): Izražava sve elemente unutar navedenog opsega. Na primjer, knjiga[*] označava sve čvorove unutar a knjiga niz.

3.3. Funkcije i filtri

JsonPath također ima funkcije koje se mogu koristiti do kraja puta za sintezu izlaznih izraza te staze: min (), maks. (), prosj (), stddev (), duljina ().

Napokon - imamo filtere; ovo su logički izrazi za ograničavanje vraćenih popisa čvorova samo na one koji trebaju metode pozivanja.

Nekoliko primjera su jednakost (==), podudaranje regularnog izraza (=~), uključenje, Ubrajanje (u), provjerite prazninu (prazan). Filteri se uglavnom koriste za predikate.

Potpuni popis i detaljna objašnjenja različitih operatora, funkcija i filtara potražite u projektu JsonPath GitHub.

4. Operacije

Prije nego započnemo s operacijama, kratka napomena - ovaj odjeljak koristi strukturu primjera JSON-a koju smo prethodno definirali.

4.1. Pristup dokumentima

JsonPath ima prikladan način pristupa JSON dokumentima, koji je putem statike čitati Apis:

 T JsonPath.read (String jsonString, String jsonPath, Predicate ... filtri);

The čitati API-ji mogu raditi sa statičnim fluidnim API-ima kako bi pružili veću fleksibilnost:

 T JsonPath.parse (String jsonString) .read (String jsonPath, Predicate ... filtri);

Ostale preopterećene inačice čitati mogu se koristiti za različite vrste JSON izvora, uključujući Objekt, InputStream, URL, i Datoteka.

Da pojednostavnimo stvari, test za ovaj dio ne uključuje predikate na popisu parametara (prazno varargi); predikati bit će razmatrano u sljedećim pododjeljcima.

Počnimo s definiranjem dva uzorka putova na kojima ćemo raditi:

Niz jsonpathCreatorNamePath = "$ ['tool'] ['jsonpath'] ['creator'] ['name']"; Niz jsonpathCreatorLocationPath = "$ ['tool'] ['jsonpath'] ['creator'] ['location'] [*]";

Dalje ćemo stvoriti DocumentContext objekt raščlanjivanjem datog JSON izvora jsonDataSourceString. Novostvoreni objekt tada će se koristiti za čitanje sadržaja koristeći gore definirane staze:

DocumentContext jsonContext = JsonPath.parse (jsonDataSourceString); Niz jsonpathCreatorName = jsonContext.read (jsonpathCreatorNamePath); Popis jsonpathCreatorLocation = jsonContext.read (jsonpathCreatorLocationPath);

Prvi čitati API vraća a Niz koji sadrži ime kreatora JsonPath, dok drugi vraća popis njegovih adresa. I koristit ćemo JUnit Tvrditi API za potvrdu rada metoda kako se očekivalo:

assertEquals ("Jayway Inc.", jsonpathCreatorName); assertThat (jsonpathCreatorLocation.toString (), containsString ("Malmo")); assertThat (jsonpathCreatorLocation.toString (), containsString ("San Francisco")); assertThat (jsonpathCreatorLocation.toString (), containsString ("Helsingborg"));

4.2. Predikati

Sad kad smo završili s osnovama, definirajmo novi JSON primjer na kojem ćemo raditi i ilustrirati stvaranje i upotrebu predikata:

{"book": [{"title": "Beginning JSON", "author": "Ben Smith", "price": 49.99}, {"title": "JSON at Work", "author": "Tom Marrs "," price ": 29,99}, {" title ":" Learn JSON in a DAY "," author ":" Acodemy "," price ": 8.99}, {" title ":" JSON: Pitanja i odgovori ", "autor": "George Duckett", "cijena": 6,00}], "raspon cijena": {"jeftino": 10,00, "srednje": 20,00}}

Predikati određuju istinite ili lažne ulazne vrijednosti za filtre kako bi suzili vraćene popise samo na podudarne objekte ili nizove. A Predikat može se lako integrirati u filtar uporabom kao argumenta za njegovu statičku tvorničku metodu. Zatraženi sadržaj se tada može pročitati iz JSON niza filtar:

Filtriraj skupiFilter = Filter.filter (Criteria.where ("cijena"). Gt (20.00)); Popis skupo = JsonPath.parse (jsonDataSourceString) .read ("$ ['book'] [?]", skupiFilter); preicateUsageAssertionHelper (skupo);

Možemo također definirati naše prilagođene Predikat i koristiti ga kao argument za čitati API:

Predikat skupoPredicate = novi Predikat () {javna logička primjena (kontekst PredicateContext) {Vrijednost niza = context.item (Map.class) .get ("cijena"). ToString (); return Float.valueOf (value)> 20,00; }}; Popis skupo = JsonPath.parse (jsonDataSourceString) .read ("$ ['book'] [?]", skupiPredicate); preicateUsageAssertionHelper (skupo);

Konačno, predikat se može izravno primijeniti na čitati API bez stvaranja bilo kakvih objekata, koji se naziva inline predikat:

Popis skupo = JsonPath.parse (jsonDataSourceString) .read ("$ ['book'] [? (@ ['price']> $ ['range of range]] [' medium '])]"); preicateUsageAssertionHelper (skupo);

Sve tri Predikat gornji primjeri se provjeravaju pomoću sljedeće metode pomoćnog utvrđivanja:

private void predicateUsageAssertionHelper (navesti predikat) {assertThat (predicate.toString (), containsString ("Beginning JSON")); assertThat (preicate.toString (), containsString ("JSON na poslu")); assertThat (preicate.toString (), ne (containsString ("Naučite JSON u DANU"))); assertThat (preicate.toString (), ne (containsString ("JSON: Pitanja i odgovori"))); }

5. Konfiguracija

5.1. Opcije

Jayway JsonPath nudi nekoliko mogućnosti za podešavanje zadane konfiguracije:

  • Opcija.AS_PATH_LIST: Vraća staze pogodaka za procjenu umjesto njihovih vrijednosti.
  • Opcija.DEFAULT_PATH_LEAF_TO_NULL: Vraća nulu za nedostajuće lišće.
  • Opcija.ALWAYS_RETURN_LIST: Vraća popis čak i kad je put definitivan.
  • Opcija.SUPPRESS_EXCEPTIONS: Osigurava da se iz procjene putanje ne šire izuzeci.
  • Opcija.REQUIRE_SVOJSTVA: Zahtijeva svojstva definirana u putu kada se procjenjuje neodređeni put.

Evo kako Opcija primjenjuje se od nule:

Konfiguracija konfiguracije = Configuration.builder (). Options (Option.). Build ();

i kako ga dodati u postojeću konfiguraciju:

Konfiguracija newConfiguration = configuration.addOptions (opcija);

5.2. SPI

Zadana konfiguracija JsonPath-a uz pomoć Opcija trebao biti dovoljan za većinu zadataka. Međutim, korisnici sa složenijim slučajevima upotrebe mogu izmijeniti ponašanje JsonPath-a u skladu sa svojim specifičnim zahtjevima - koristeći tri različita SPI-a:

  • JsonProvider SPI: Omogućuje nam promjenu načina na koji JsonPath raščlanjuje i obrađuje JSON dokumente
  • MappingProvider SPI: Omogućava prilagodbu veza između vrijednosti čvora i vraćenih tipova objekata
  • CacheProvider SPI: podešava načine predmemoriranja staza, što može pomoći u povećanju performansi

6. Primjer slučajeva korištenja

Sad kad dobro razumijemo funkcionalnost za koju se JsonPath može koristiti - pogledajmo primjer.

Ovaj odjeljak ilustrira rad s JSON podacima vraćenim s web usluge - pretpostavimo da imamo uslugu informacija o filmovima koja vraća sljedeću strukturu:

[{"id": 1, "title": "Casino Royale", "director": "Martin Campbell", "glumi": ["Daniel Craig", "Eva Green"], "desc": "Dvadeset i prvi Film o Jamesu Bondu "," datum izlaska ": 1163466000000," blagajna ": 594275385}, {" id ": 2," title ":" Quantum of Solace "," director ":" Marc Forster "," starring ": ["Daniel Craig", "Olga Kurylenko"], "desc": "Dvadeset drugi film o Jamesu Bondu", "datum izlaska": 1225242000000, "blagajna": 591692078}, {"id": 3, "naslov" : "Skyfall", "director": "Sam Mendes", "glumi": ["Daniel Craig", "Naomie Harris"], "desc": "Dvadeset i treći film o Jamesu Bondu", "datum izlaska": 1350954000000, "blagajna": 1110526981}, {"id": 4, "title": "Spectre", "director": "Sam Mendes", "starring": ["Daniel Craig", "Lea Seydoux"], "desc ":" Dvadeset i četvrti film o Jamesu Bondu "," datum izlaska ": 1445821200000," blagajna ": 879376275}]

Gdje je vrijednost Datum izlaska polje je trajanje od epohe u milisekundama i blagajna je prihod filma u kinu u američkim dolarima.

Obrađivat ćemo pet različitih radnih scenarija povezanih s GET zahtjevima, pretpostavljajući da je gornja JSON hijerarhija izvučena i pohranjena u Niz varijabla imenovana jsonString.

6.1. Dobivanje podataka o objektima s obzirom na ID-ove

U ovom slučaju upotrebe, klijent zahtijeva detaljne informacije o određenom filmu dajući poslužitelju točne podatke iskaznica onog. Ovaj primjer pokazuje kako poslužitelj traži tražene podatke prije vraćanja klijentu.

Recimo da moramo pronaći zapis s iskaznica jednak 2. Ispod je prikazano kako se postupak provodi i testira.

Prvi korak je odabrati točan objekt podataka:

Object dataObject = JsonPath.parse (jsonString) .read ("$ [? (@. Id == 2)]"); Niz dataString = dataObject.toString ();

JUnit Tvrditi API potvrđuje postojanje nekoliko polja:

assertThat (dataString, containsString ("2")); assertThat (dataString, containsString ("Quantum of Solace")); assertThat (dataString, containsString ("Dvadeset drugi film o Jamesu Bondu"));

6.2. Dobivanje naslova filma s obzirom na glavnu ulogu

Recimo da želimo potražiti film u kojem glumi glumica zvana Eva Green. Poslužitelj se mora vratiti titula filma koji Eva Green je uključen u glumi niz.

Sljedeći test ilustrirat će kako to učiniti i potvrditi vraćeni rezultat:

@Test javna praznina givenStarring_whenRequestingMovieTitle_thenSucceed () {Popis dataList = JsonPath.parse (jsonString) .read ("$ [? ('Eva Green' u @ ['starring'])]"); Naslov niza = (Niz) dataList.get (0) .get ("naslov"); assertEquals ("Casino Royale", naslov); }

6.3. Izračun ukupnog prihoda

Ovaj scenarij koristi funkciju JsonPath koja se naziva duljina () da biste izračunali broj filmskih zapisa, izračunali ukupni prihod svih filmova. Implementacija i testiranje prikazani su na sljedeći način:

@Test javna praznina givenCompleteStructure_whenCalculatingTotalRevenue_thenSucceed () {DocumentContext context = JsonPath.parse (jsonString); int dužina = context.read ("$. length ()"); dugi prihod = 0; for (int i = 0; i <length; i ++) {prihod + = context.read ("$ [" + i + "] ['blagajna']", Long.class); } assertEquals (594275385L + 591692078L + 1110526981L + 879376275L, prihod); }

6.4. Film s najvećim prihodom

Ovaj slučaj upotrebe ilustrira upotrebu ne-zadane opcije konfiguracije JsonPath, naime Opcija.AS_PATH_LIST, kako biste saznali film s najvećim prihodom. Konkretni koraci opisani su u nastavku.

Prvo moramo izdvojiti popis prihoda od blagajne svih filmova, a zatim ga pretvoriti u niz za sortiranje:

DocumentContext context = JsonPath.parse (jsonString); Popis prihodaList = context.read ("$ [*] ['blagajna']"); Integer [] prihodArray = prihodList.toArray (novi Integer [0]); Arrays.sort (prihodArray);

The najviši prihod varijabla se lako može preuzeti s prihodArray sortirani niz, a zatim se koristi za obradu puta do zapisa filma s najvećim prihodom:

int najvišiRevenue = prihodArray [prihodArray.length - 1]; Konfiguracijski putConfiguration = Configuration.builder (). Options (Option.AS_PATH_LIST) .build (); Popis pathList = JsonPath.using (pathConfiguration) .parse (jsonString) .read ("$ [? (@ ['Blagajna'] ==" + najviši prihod + ")]");

Na temelju tog izračunatog puta, titula odgovarajućeg filma može se odrediti i vratiti:

Map dataRecord = context.read (pathList.get (0)); Naslov niza = dataRecord.get ("naslov");

Cijeli postupak potvrđuje Tvrditi API:

assertEquals ("Skyfall", naslov);

6.5. Najnoviji film redatelja

Ovaj primjer ilustrirat će način otkrivanja trajnog filma koji je režirao redatelj Sam Mendes.

Za početak, popis svih filmova u režiji Sam Mendes je stvoreno:

DocumentContext context = JsonPath.parse (jsonString); Popis dataList = context.read ("$ [? (@. director == 'Sam Mendes')]");

Taj se popis koristi za izdvajanje datuma izdanja. Ti će se datumi pohraniti u niz, a zatim sortirati:

Popis dateList = novi ArrayList (); za (Stavka karte: dataList) {Datum objekta = item.get ("datum izdavanja"); dateList.add (datum); } Long [] dateArray = dateList.toArray (novi Long [0]); Nizovi.sort (dateArray);

The najnovije vrijeme varijabla, koja je posljednji element razvrstanog niza, koristi se u kombinaciji s direktor vrijednost polja za određivanje titula traženog filma:

long latestTime = dateArray [dateArray.length - 1]; Popis finalDataList = context.read ("$ [? (@ ['director'] == 'Sam Mendes' && @ ['date release'] ==" + latestTime + ")]"); Naslov niza = (Niz) finalDataList.get (0) .get ("naslov");

Sljedeća je tvrdnja dokazala da sve funkcionira prema očekivanjima:

assertEquals ("Spectre", naslov);

7. Zaključak

Ovaj je vodič obuhvatio temeljne značajke Jaywaya JsonPath - moćnog alata za prelazak i raščlanjivanje JSON dokumenata.

Iako JsonPath ima neke nedostatke, poput nedostatka operatora za dosezanje roditeljskih ili bratskih čvorova, može biti vrlo koristan u mnogim scenarijima.

Provedbu svih ovih primjera i isječaka koda možete pronaći u a GitHub projekt.