Rad s čvorovima modela stabla u Jacksonu

1. Pregled

Ovaj tutorial usredotočit će se na rad s čvorovi modela stabla u Jacksonu.

Koristit ćemo JsonNode za razne pretvorbe, kao i za dodavanje, izmjenu i uklanjanje čvorova.

2. Stvaranje čvora

Prvi korak u stvaranju čvora je instanciranje ObjectMapper objekt pomoću zadanog konstruktora:

Mapa ObjectMapper = novi ObjectMapper ();

Od stvaranja ObjectMapper Objekt je skup, preporučuje se da se isti koristi za više operacija.

Dalje, imamo tri različita načina za stvaranje čvora stabla nakon što imamo svoj ObjectMapper.

2.1. Konstruirajte čvor od nule

Najčešći način stvaranja čvora iz ničega je sljedeći:

JsonNode čvor = mapper.createObjectNode ();

Alternativno, čvor možemo stvoriti i putem JsonNodeFactory:

JsonNode čvor = JsonNodeFactory.instance.objectNode ();

2.2. Analiza iz JSON izvora

Ova je metoda dobro obrađena u članku Jackson - Marshall String to JsonNode. Pozovite ga ako vam trebaju dodatne informacije.

2.3. Pretvori iz objekta

Čvor se može pretvoriti iz Java objekta pozivanjem datoteke valueToTree (objekt iz vrijednosti) metoda na ObjectMapper:

JsonNode čvor = mapper.valueToTree (fromValue);

The convertValue API je ovdje također koristan:

JsonNode čvor = mapper.convertValue (fromValue, JsonNode.class);

Pogledajmo kako to funkcionira u praksi. Pretpostavimo da imamo razred s imenom NodeBean:

javna klasa NodeBean {private int id; privatni naziv niza; javni NodeBean () {} javni NodeBean (int id, naziv niza) {this.id = id; this.name = ime; } // standardni getteri i postavljači}

Napišimo test koji osigurava da se pretvorba odvija ispravno:

@Test javna praznina givenAnObject_whenConvertingIntoNode_thenCorrect () {NodeBean fromValue = new NodeBean (2016, "baeldung.com"); JsonNode čvor = mapper.valueToTree (fromValue); assertEquals (2016, node.get ("id"). intValue ()); assertEquals ("baeldung.com", node.get ("name"). textValue ()); }

3. Transformacija čvora

3.1. Napišite kao JSON

Osnovna metoda za pretvaranje čvora stabla u JSON niz je sljedeća:

mapper.writeValue (odredište, čvor);

gdje odredište može biti a Datoteka, an Izlazni tok ili a Pisac.

Ponovnom upotrebom predavanja NodeBean deklarirano u odjeljku 2.3, test osigurava da ova metoda radi kako se očekuje:

konačni String pathToTestFile = "node_to_json_test.json"; @Test public void givenANode_whenModifyingIt_thenCorrect () baca IOException {String newString = "{\" nick \ ": \" cowtowncoder \ "}"; JsonNode newNode = mapper.readTree (newString); JsonNode rootNode = ExampleStructure.getExampleRoot (); ((ObjectNode) rootNode) .set ("name", newNode); assertFalse (rootNode.path ("name"). path ("nick"). isMissingNode ()); assertEquals ("cowtowncoder", rootNode.path ("name"). path ("nick"). textValue ()); }

3.2. Pretvori u objekt

Najprikladniji način pretvorbe a JsonNode u Java objekt je treeToValue API:

NodeBean toValue = mapper.treeToValue (čvor, NodeBean.class);

Što je funkcionalno ekvivalentno:

NodeBean toValue = mapper.convertValue (čvor, NodeBean.class)

To također možemo učiniti kroz tok tokena:

JsonParser parser = mapper.treeAsTokens (čvor); NodeBean toValue = mapper.readValue (parser, NodeBean.class);

Na kraju, provedimo test koji potvrđuje postupak pretvorbe:

@Test public void givenANode_whenConvertingIntoAnObject_thenCorrect () baca JsonProcessingException {JsonNode node = mapper.createObjectNode (); ((ObjectNode) čvor) .put ("id", 2016); ((ObjectNode) čvor) .put ("ime", "baeldung.com"); NodeBean toValue = mapper.treeToValue (čvor, NodeBean.class); assertEquals (2016, toValue.getId ()); assertEquals ("baeldung.com", toValue.getName ()); }

4. Manipuliranje čvorovima stabla

Sljedeći JSON elementi, sadržani u datoteci s imenom primjer.json, koriste se kao osnovna struktura za radnje raspravljene u ovom odjeljku koje treba poduzeti na:

{"name": {"first": "Tatu", "last": "Saloranta"}, "title": "Jackson osnivač", "company": "FasterXML"}

Ova JSON datoteka, smještena na stazi razreda, raščlanjena je u stablo modela:

javna klasa ExampleStructure {private static ObjectMapper mapper = new ObjectMapper (); statički JsonNode getExampleRoot () baca IOException {InputStream exampleInput = ExampleStructure.class.getClassLoader () .getResourceAsStream ("example.json"); JsonNode rootNode = mapper.readTree (exampleInput); vratiti rootNode; }}

Primijetite da će se korijen stabla koristiti za ilustriranje operacija na čvorovima u sljedećim pododjeljcima.

4.1. Lociranje čvora

Prije rada na bilo kojem čvoru, prvo što moramo učiniti je pronaći i dodijeliti ga varijabli.

Ako je put do čvora poznat unaprijed, to je prilično lako učiniti. Na primjer, recimo da želimo čvor s imenom posljednji, koja je pod Ime čvor:

JsonNode locatedNode = rootNode.path ("name"). Path ("last");

Alternativno, dobiti ili s API se također mogu koristiti umjesto staza.

Ako put nije poznat, pretraga će, naravno, postati složenija i iterativnija.

Primjer ponavljanja svih čvorova možemo vidjeti u 5. Iteriranje preko čvorova

4.2. Dodavanje novog čvora

Čvor se može dodati kao dijete drugog čvora kako slijedi:

ObjectNode newNode = ((ObjectNode) locatedNode) .put (FieldName, value);

Mnoge preopterećene inačice staviti može se koristiti za dodavanje novih čvorova različitih vrsta vrijednosti.

Dostupne su i mnoge druge slične metode, uključujući putArray, putObject, PutPOJO, putRawValue i putNull.

Na kraju - pogledajmo primjer - gdje korijenskom čvoru stabla dodajemo cijelu strukturu:

"address": {"city": "Seattle", "state": "Washington", "country": "United States"}

Evo cijelog testa koji prolazi kroz sve ove operacije i provjerava rezultate:

@Test public void givenANode_whenAddingIntoATree_thenCorrect () baca IOException {JsonNode rootNode = ExampleStructure.getExampleRoot (); ObjectNode addedNode = ((ObjectNode) rootNode) .putObject ("adresa"); addedNode .put ("grad", "Seattle") .put ("država", "Washington") .put ("država", "Sjedinjene Države"); assertFalse (rootNode.path ("adresa"). isMissingNode ()); assertEquals ("Seattle", rootNode.path ("adresa"). path ("grad"). textValue ()); assertEquals ("Washington", rootNode.path ("adresa"). path ("state"). textValue ()); assertEquals ("Sjedinjene Države", rootNode.path ("adresa"). path ("country"). textValue ();}

4.3. Uređivanje čvora

An ObjectNode instanca može se izmijeniti pozivanjem set (Ime polja polja, vrijednost JsonNode) metoda:

JsonNode locatedNode = locatedNode.set (Ime polja, vrijednost);

Slični se rezultati mogu postići uporabom zamijeniti ili postavitiSve metode na objektima iste vrste.

Da bismo provjerili radi li metoda kako se očekivalo, promijenit ćemo vrijednost polja Ime pod korijenskim čvorom od objekta prvi i posljednji u drugu koja se sastoji od samo nadimak polje u testu:

@Test public void givenANode_whenModifyingIt_thenCorrect () baca IOException {String newString = "{\" nick \ ": \" cowtowncoder \ "}"; JsonNode newNode = mapper.readTree (newString); JsonNode rootNode = ExampleStructure.getExampleRoot (); ((ObjectNode) rootNode) .set ("name", newNode); assertFalse (rootNode.path ("name"). path ("nick"). isMissingNode ()); assertEquals ("cowtowncoder", rootNode.path ("name"). path ("nick"). textValue ()); }

4.4. Uklanjanje čvora

Čvor se može ukloniti pozivanjem ukloni (Ime polja polja) API na nadređenom čvoru:

JsonNode removeNode = locatedNode.remove (FieldName);

Da bismo odjednom uklonili više čvorova, možemo pozvati preopterećenu metodu s parametrom Kolekcija type, koji vraća nadređeni čvor umjesto onog koji će se ukloniti:

ObjectNode locatedNode = locatedNode.remove (fieldNames);

U krajnjem slučaju kada želimo izbrisati sve podvrste datog čvora the ukloniti sve API mi dobro dođe.

Sljedeći test usredotočit će se na prvu gore spomenutu metodu - koja je najčešći scenarij:

@Test public void givenANode_whenRemovingFromATree_thenCorrect () baca IOException {JsonNode rootNode = ExampleStructure.getExampleRoot (); ((ObjectNode) rootNode) .remove ("tvrtka"); assertTrue (rootNode.path ("tvrtka"). isMissingNode ()); }

5. Iteracija nad čvorovima

Premotajmo sve čvorove u JSON dokumentu i preformatiramo ih u YAML. JSON ima tri vrste čvora, a to su Value, Object i Array.

Dakle, pobrinimo se da naši uzorci podataka sadrže sve tri različite vrste dodavanjem Niz:

{"name": {"first": "Tatu", "last": "Saloranta"}, "title": "Jackson osnivač", "company": "FasterXML", "pets": [{"type": "pas", "broj": 1}, {"tip": "riba", "broj": 50}]}

Sada, da vidimo YAML koji želimo proizvesti:

ime: prvo: Tatu posljednje: Saloranta naslov: Jackson osnivač tvrtka: FasterXML kućni ljubimci: - tip: broj psa: 1 - tip: broj ribe: 50

Znamo da JSON čvorovi imaju hijerarhijsku strukturu stabla. Dakle, najlakši način ponavljanja cijelog JSON dokumenta je započeti s vrha i proći kroz sve podređene čvorove.

Korijenski čvor ćemo prenijeti u rekurzivnu metodu. Tada će se metoda pozvati sa svakim podređenim dijelom isporučenog čvora.

5.1. Testiranje ponavljanja

Započet ćemo s izradom jednostavnog testa koji provjerava možemo li JSON uspješno pretvoriti u YAML.

Naš test isporučuje korijenski čvor JSON dokumenta našem toYaml metoda i tvrdi da je vraćena vrijednost ono što očekujemo:

@Test public void givenANodeTree_whenIteratingSubNodes_thenWeFindExpected () baca IOException {JsonNode rootNode = ExampleStructure.getExampleRoot (); Niz yaml = onTest.toYaml (rootNode); assertEquals (očekivaniYaml, yaml); } javni String toYaml (korijen JsonNode) {StringBuilder yaml = novi StringBuilder (); processNode (root, yaml, 0); vratiti yaml.toString (); }}

5.2. Rukovanje različitim vrstama čvorova

Moramo malo drugačije postupati s različitim vrstama čvorova. Učinit ćemo to u našem processNode metoda:

privatna void procesNode (JsonNode jsonNode, StringBuilder yaml, int dubina) {if (jsonNode.isValueNode ()) {yaml.append (jsonNode.asText ()); } else if (jsonNode.isArray ()) {for (JsonNode arrayItem: jsonNode) {appendNodeToYaml (arrayItem, yaml, dubina, istina); }} else if (jsonNode.isObject ()) {appendNodeToYaml (jsonNode, yaml, dubina, lažno); }}

Prvo, razmotrimo čvor Value. Jednostavno zovemo asText metoda čvora za dobivanje a Niz prikaz vrijednosti.

Dalje, pogledajmo čvor Array. Svaka stavka unutar čvora Array sama je po sebi JsonNode, pa prelazimo preko polja i prosljeđujemo svaki čvor u appendNodeToYaml metoda. Također moramo znati da su ti čvorovi dio niza.

Nažalost, sam čvor ne sadrži ništa što nam to govori, pa ćemo proslijediti zastavicu u naš appendNodeToYaml metoda.

Konačno, želimo ponoviti sve podređene čvorove svakog čvora Object. Jedna od mogućnosti je upotreba JsonNode.elementi. Međutim, ne možemo odrediti ime polja iz elementa jer ono sadrži samo vrijednost polja:

Objekt {"first": "Tatu", "last": "Saloranta"} Vrijednost "Jackson Founder" Value "FasterXML" Array [{"type": "dog", "number": 1}, {"type": "riba", "broj": 50}]

Umjesto toga, mi ćemo koristiti JsonNode.polja jer nam ovo daje pristup i imenu polja i vrijednosti:

Key = "name", Value = Object {"first": "Tatu", "last": "Saloranta"} Key = "title", Value = Value "Jackson Founder" Key = "company", Value = Value "FasterXML "Key =" pets ", Value = Array [{" type ":" dog "," number ": 1}, {" type ":" fish "," number ": 50}]

Za svako polje dodamo naziv polja u izlaz. Zatim obradite vrijednost kao podređeni čvor prosljeđivanjem u processNode metoda:

private void appendNodeToYaml (čvor JsonNode, yaml StringBuilder, int dubina, logički isArrayItem) {Iterator polja = čvor.polja (); boolean isFirst = true; while (fields.hasNext ()) {Entry jsonField = fields.next (); addFieldNameToYaml (yaml, jsonField.getKey (), dubina, isArrayItem && isFirst); processNode (jsonField.getValue (), yaml, dubina + 1); isFirst = false; }}

Iz čvora ne možemo reći koliko predaka ima. Tako prolazimo polje zvano dubina u processNode metoda za praćenje ovoga. Ovu vrijednost povećavamo svaki put kad dobijemo podređeni čvor kako bismo mogli pravilno uvlačiti polja u našem YAML izlazu:

private void addFieldNameToYaml (StringBuilder yaml, StringName polja, int dubina, logički isFirstInArray) {if (yaml.length ()> 0) {yaml.append ("\ n"); int requiredDepth = (isFirstInArray)? dubina-1: dubina; for (int i = 0; i <requiredDepth; i ++) {yaml.append (""); } if (isFirstInArray) {yaml.append ("-"); }} yaml.append (Ime polja); yaml.append (":"); }

Sad kad imamo sav kôd na mjestu da se prelistava preko čvorova i kreira YAML izlaz, možemo pokrenuti test kako bismo pokazali da radi.

6. Zaključak

Ovaj je vodič obuhvatio uobičajene API-je i scenarije rada s modelom stabla u Jacksonu.

I, kao i uvijek, implementaciju svih ovih primjera i isječaka koda možete pronaći u na GitHubu - ovo je projekt zasnovan na Mavenu, pa bi ga trebalo biti lako uvesti i pokrenuti kakav jest.