Arbeider med Tree Model Nodes i Jackson

1. Oversikt

Denne opplæringen vil fokusere på å jobbe med tremodellnoder i Jackson.

Vi bruker JsonNode for ulike konverteringer samt legge til, endre og fjerne noder.

2. Opprette en node

Det første trinnet i opprettelsen av en node er å sette i gang en ObjectMapper objekt ved hjelp av standardkonstruktøren:

ObjectMapper mapper = ny ObjectMapper ();

Siden opprettelsen av en ObjectMapper objektet er dyrt, anbefales det at den samme brukes på nytt for flere operasjoner.

Deretter har vi tre forskjellige måter å lage en treknute når vi har fått vår ObjectMapper.

2.1. Konstruer en node fra bunnen av

Den vanligste måten å lage en node ut av ingenting er som følger:

JsonNode-node = mapper.createObjectNode ();

Alternativt kan vi også opprette en node via JsonNodeFactory:

JsonNode node = JsonNodeFactory.instance.objectNode ();

2.2. Parse fra en JSON-kilde

Denne metoden er godt dekket i artikkelen Jackson - Marshall String to JsonNode. Vennligst referer til den hvis du trenger mer info.

2.3. Konverter fra et objekt

En node kan konverteres fra et Java-objekt ved å ringe valueToTree (Object fromValue) metoden på ObjectMapper:

JsonNode-node = mapper.valueToTree (fromValue);

De convertValue API er også nyttig her:

JsonNode node = mapper.convertValue (fromValue, JsonNode.class);

La oss se hvordan det fungerer i praksis. Anta at vi har en klasse som heter NodeBean:

offentlig klasse NodeBean {privat int id; privat strengnavn; offentlig NodeBean () {} offentlig NodeBean (int id, strengnavn) {this.id = id; this.name = navn; } // standard getters and setters}

La oss skrive en test som sørger for at konverteringen skjer riktig:

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

3. Transformere en node

3.1. Skriv ut som JSON

Den grunnleggende metoden for å transformere en treknute til en JSON-streng er følgende:

mapper.writeValue (destinasjon, node);

hvor destinasjonen kan være en Fil, en OutputStream eller a Forfatter.

Ved å gjenbruke klassen NodeBean erklært i avsnitt 2.3, sørger en test for at denne metoden fungerer som forventet:

endelig String pathToTestFile = "node_to_json_test.json"; @Test offentlig ugyldighet gittANode_whenModifyingIt_thenCorrect () kaster IOException {String newString = "{\" nick \ ": \" cowtowncoder \ "}"; JsonNode newNode = mapper.readTree (newString); JsonNode rootNode = ExampleStructure.getExampleRoot (); ((ObjectNode) rootNode) .set ("navn", newNode); assertFalse (rootNode.path ("navn"). sti ("nick"). erMissingNode ()); assertEquals ("cowtowncoder", rootNode.path ("name"). sti ("nick"). textValue ()); }

3.2. Konverter til et objekt

Den mest praktiske måten å konvertere en JsonNode inn i et Java-objekt er treeToValue API:

NodeBean toValue = mapper.treeToValue (node, NodeBean.class);

Som funksjonelt tilsvarer:

NodeBean toValue = mapper.convertValue (node, NodeBean.class)

Vi kan også gjøre det gjennom en token stream:

JsonParser-parser = mapper.treeAsTokens (node); NodeBean toValue = mapper.readValue (parser, NodeBean.class);

Til slutt, la oss implementere en test som bekrefter konverteringsprosessen:

@Test offentlig ugyldighet gittANode_whenConvertingIntoAnObject_thenCorrect () kaster JsonProcessingException {JsonNode node = mapper.createObjectNode (); ((ObjectNode) node) .put ("id", 2016); ((ObjectNode) node) .put ("navn", "baeldung.com"); NodeBean toValue = mapper.treeToValue (node, NodeBean.class); assertEquals (2016, toValue.getId ()); assertEquals ("baeldung.com", toValue.getName ()); }

4. Manipulere treknuter

Følgende JSON-elementer, inneholdt i en fil med navnet eksempel.json, brukes som en basisstruktur for handlinger som er diskutert i dette avsnittet, og som skal tas på:

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

Denne JSON-filen, som ligger på klassestien, blir analysert i et modelltre:

public class ExampleStructure {private static ObjectMapper mapper = new ObjectMapper (); statisk JsonNode getExampleRoot () kaster IOException {InputStream eksempelInput = ExampleStructure.class.getClassLoader () .getResourceAsStream ("eksempel.json"); JsonNode rootNode = mapper.readTree (eksempelInput); returnere rootNode; }}

Merk at roten til treet vil bli brukt når du illustrerer operasjoner på noder i de følgende underavsnittene.

4.1. Finne en node

Før vi jobber med en hvilken som helst node, er det første vi trenger å gjøre å finne og tildele den til en variabel.

Hvis banen til noden er kjent på forhånd, er det ganske enkelt å gjøre. Si for eksempel at vi vil ha en node som heter siste, som er under Navn node:

JsonNode locatedNode = rootNode.path ("navn"). Sti ("siste");

Alternativt kan eller med APIer kan også brukes i stedet for sti.

Hvis banen ikke er kjent, vil søket selvfølgelig bli mer komplisert og iterativt.

Vi kan se et eksempel på å gjenta over alle nodene i 5. Iterere over nodene

4.2. Legge til en ny node

En node kan legges til som barn av en annen node som følger:

ObjectNode newNode = ((ObjectNode) locatedNode) .put (feltnavn, verdi);

Mange overbelastede varianter av sette kan brukes til å legge til nye noder av forskjellige verdityper.

Mange andre lignende metoder er også tilgjengelige, inkludert putArray, putObject, PutPOJO, putRawValue og putNull.

Til slutt - la oss ta en titt på et eksempel - der vi legger til en hel struktur i rotnoden til treet:

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

Her er den fullstendige testen som går gjennom alle disse operasjonene og verifiserer resultatene:

@Test offentlig ugyldighet gittANode_whenAddingIntoATree_thenCorrect () kaster IOException {JsonNode rootNode = ExampleStructure.getExampleRoot (); ObjectNode addedNode = ((ObjectNode) rootNode) .putObject ("adresse"); addedNode .put ("city", "Seattle") .put ("state", "Washington") .put ("country", "United States"); assertFalse (rootNode.path ("adresse"). erMissingNode ()); assertEquals ("Seattle", rootNode.path ("adresse"). sti ("by"). textValue ()); assertEquals ("Washington", rootNode.path ("adresse"). sti ("stat"). textValue ()); assertEquals ("USA", rootNode.path ("adresse"). sti ("land"). textValue ();}

4.3. Redigere en node

An ObjectNode forekomst kan endres ved å påkalle sett (streng feltnavn, JsonNode-verdi) metode:

JsonNode liggerNode = lokalisertNode.sett (feltnavn, verdi);

Lignende resultater kan oppnås ved å bruke erstatte eller settAll metoder på gjenstander av samme type.

For å verifisere at metoden fungerer som forventet, vil vi endre verdien av feltet Navn under rotnoden fra et objekt av først og siste inn i en annen bestående av bare nick felt i en test:

@Test offentlig ugyldighet gittANode_whenModifyingIt_thenCorrect () kaster IOException {String newString = "{\" nick \ ": \" cowtowncoder \ "}"; JsonNode newNode = mapper.readTree (newString); JsonNode rootNode = ExampleStructure.getExampleRoot (); ((ObjectNode) rootNode) .set ("navn", newNode); assertFalse (rootNode.path ("navn"). sti ("nick"). erMissingNode ()); assertEquals ("cowtowncoder", rootNode.path ("name"). sti ("nick"). textValue ()); }

4.4. Fjerne en node

En node kan fjernes ved å ringe fjern (streng feltnavn) API på den overordnede noden:

JsonNode fjernetNode = locatedNode.remove (fieldName);

For å fjerne flere noder samtidig, kan vi påkalle en overbelastet metode med parameteren Samling type, som returnerer foreldrenoden i stedet for den som skal fjernes:

ObjectNode locatedNode = locatedNode.remove (fieldNames);

I ekstreme tilfeller når vi vil slette alle undernoder til en gitt node de Fjern alle API kommer godt med.

Følgende test vil fokusere på den første metoden som er nevnt ovenfor - som er det vanligste scenariet:

@Test offentlig ugyldighet gittANode_whenRemovingFromATree_thenCorrect () kaster IOException {JsonNode rootNode = ExampleStructure.getExampleRoot (); ((ObjectNode) rootNode) .remove ("selskap"); assertTrue (rootNode.path ("selskap"). isMissingNode ()); }

5. Iterere over nodene

La oss gjenta alle nodene i et JSON-dokument og omformatere dem til YAML. JSON har tre typer noder, som er Verdi, Objekt og Array.

Så la oss sikre at eksempeldataene våre har alle de tre forskjellige typene ved å legge til en Array:

{"name": {"first": "Tatu", "last": "Saloranta"}, "title": "Jackson grunnlegger", "company": "FasterXML", "pets": [{"type": "hund", "nummer": 1}, {"type": "fisk", "nummer": 50}]}

La oss nå se YAML vi ønsker å produsere:

navn: første: Tatu siste: Saloranta tittel: Jackson grunnlegger selskap: FasterXML kjæledyr: - type: hundenummer: 1 - type: fisk nummer: 50

Vi vet at JSON-noder har en hierarkisk trestruktur. Så, den enkleste måten å gjenta hele JSON-dokumentet er å starte på toppen og jobbe oss nedover gjennom alle noder til barnet.

Vi overfører rotnoden til en rekursiv metode. Metoden vil da kalle seg selv med hvert barn av den leverte noden.

5.1. Testing av Iterasjonen

Vi starter med å lage en enkel test som sjekker at vi kan konvertere JSON til YAML.

Testen vår leverer rotnoden til JSON-dokumentet til vår tilYaml metoden og hevder at den returnerte verdien er det vi forventer:

@Test offentlig ugyldig gittANodeTree_whenIteratingSubNodes_thenWeFindExpected () kaster IOException {JsonNode rootNode = ExampleStructure.getExampleRoot (); Streng yaml = onTest.toYaml (rootNode); assertEquals (forventet Yaml, yaml); } public String toYaml (JsonNode root) {StringBuilder yaml = new StringBuilder (); processNode (root, yaml, 0); returner yaml.toString (); }}

5.2. Håndtering av forskjellige nodetyper

Vi må håndtere forskjellige typer noder litt annerledes. Vi gjør dette i vår processNode metode:

privat ugyldig prosessNode (JsonNode jsonNode, StringBuilder yaml, int depth) {if (jsonNode.isValueNode ()) {yaml.append (jsonNode.asText ()); } annet hvis (jsonNode.isArray ()) {for (JsonNode arrayItem: jsonNode) {appendNodeToYaml (arrayItem, yaml, depth, true); }} annet hvis (jsonNode.isObject ()) {appendNodeToYaml (jsonNode, yaml, depth, false); }}

La oss først vurdere en verdinode. Vi kaller bare som Tekst metode for noden for å få en String representasjon av verdien.

Deretter, la oss se på en Array-node. Hvert element i Array-noden er i seg selv a JsonNode, så vi gjentar over matrisen og sender hver node til appendNodeToYaml metode. Vi må også vite at disse nodene er en del av en matrise.

Dessverre inneholder ikke noden noe som forteller oss det, så vi sender et flagg inn i vårt appendNodeToYaml metode.

Til slutt vil vi iterere over alle underordnede noder til hver Object-node. Et alternativ er å bruke JsonNode.elements. Vi kan imidlertid ikke bestemme feltnavnet fra et element da det bare inneholder feltverdien:

Objekt {"first": "Tatu", "last": "Saloranta"} Verdi "Jackson Grunnlegger" Value "FasterXML" Array [{"type": "dog", "number": 1}, {"type": "fisk", "nummer": 50}]

I stedet bruker vi JsonNode.fields da dette gir oss tilgang til både feltnavnet og verdien:

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}]

For hvert felt legger vi til feltnavnet i utgangen. Behandle deretter verdien som en undernode ved å sende den til processNode metode:

private void appendNodeToYaml (JsonNode node, StringBuilder yaml, int depth, boolean isArrayItem) {Iterator felt = node.fields (); boolsk isFirst = true; while (fields.hasNext ()) {Entry jsonField = fields.next (); addFieldNameToYaml (yaml, jsonField.getKey (), dybde, erArrayItem && isFirst); processNode (jsonField.getValue (), yaml, dybde + 1); isFirst = false; }}

Vi kan ikke fortelle fra noden hvor mange forfedre den har. Så vi passerer et felt som kalles dybde inn i processNode metode for å holde oversikt over dette. Vi øker denne verdien hver gang vi får en undernode, slik at vi kan rette inn feltene i YAML-utgangen vår:

privat tomrom addFieldNameToYaml (StringBuilder yaml, String fieldName, int depth, boolean isFirstInArray) {if (yaml.length ()> 0) {yaml.append ("\ n"); int requiredDepth = (isFirstInArray)? dybde-1: dybde; for (int i = 0; i <requiredDepth; i ++) {yaml.append (""); } hvis (isFirstInArray) {yaml.append ("-"); }} yaml.append (fieldName); yaml.append (":"); }

Nå som vi har all koden på plass for å iterere over nodene og lage YAML-utgangen, kan vi kjøre testen for å vise at den fungerer.

6. Konklusjon

Denne veiledningen dekket de vanlige API-ene og scenariene for å jobbe med en tremodell i Jackson.

Og som alltid kan implementeringen av alle disse eksemplene og kodebiter finnes i over på GitHub - dette er et Maven-basert prosjekt, så det skal være enkelt å importere og kjøre som det er.


$config[zx-auto] not found$config[zx-overlay] not found