Jackson - toveis forhold
1. Oversikt
I denne opplæringen vil vi gå gjennom de beste måtene å håndtere toveis forhold i Jackson.
Vi vil diskutere Jackson JSONs uendelige rekursjonsproblem, så - vi får se hvordan vi kan serieisere enheter med toveis forhold og til slutt - vi vil deserialisere dem.
2. Uendelig rekursjon
Først - la oss ta en titt på Jackson uendelig rekursjonsproblem. I det følgende eksemplet har vi to enheter - “Bruker”Og”Punkt”- med et enkelt en-til-mange forhold:
“Bruker”Enhet:
public class User {public int id; offentlig streng navn; public List userItems; }
“Punkt”Enhet:
public class Item {public int id; offentlig streng elementnavn; offentlig bruker eier; }
Når vi prøver å serieisere en forekomst av “Punkt“, Vil Jackson kaste en JsonMappingException unntak:
@Test (forventet = JsonMappingException.class) offentlig ugyldig givenBidirectionRelation_whenSerializing_thenException () kaster JsonProcessingException {Bruker bruker = ny bruker (1, "John"); Vareartikkel = nytt element (2, "bok", bruker); user.addItem (vare); ny ObjectMapper (). writeValueAsString (element); }
De fullt unntak er:
com.fasterxml.jackson.databind.JsonMappingException: Uendelig rekursjon (StackOverflowError) (gjennom referansekjede: org.baeldung.jackson.bidirection.Item ["eier"] -> org.baeldung.jackson.bidirection.User ["userItems"] -> java.util.ArrayList [0] -> org.baeldung.jackson.bidirection.Item ["eier"] ->… ..
La oss se i løpet av de neste par avsnittene - hvordan du kan løse dette problemet.
3. Bruk @JsonManagedReference, @JsonBackReference
La oss først kommentere forholdet til @JsonManagedReference, @JsonBackReference for å la Jackson bedre håndtere forholdet:
Her er “Bruker”Enhet:
public class User {public int id; offentlig streng navn; @JsonBackReference offentlig Liste userItems; }
Og "Punkt“:
public class Item {public int id; offentlig streng elementnavn; @JsonManagedReference offentlig bruker eier; }
La oss nå teste ut de nye enhetene:
@Test offentlig ugyldighet gittBidirectionRelation_whenUsingJacksonReferenceAnnotation_thenCorrect () kaster JsonProcessingException {Bruker bruker = ny bruker (1, "John"); Vareartikkel = nytt element (2, "bok", bruker); user.addItem (vare); String result = new ObjectMapper (). WriteValueAsString (item); assertThat (resultat, inneholderString ("bok")); assertThat (resultat, inneholderString ("John")); assertThat (resultat, ikke (inneholderString ("userItems"))); }
Her er utgangen av serialisering:
{"id": 2, "itemName": "book", "owner": {"id": 1, "name": "John"}}
Noter det:
- @JsonManagedReference er den fremre delen av referansen - den som serielliseres normalt.
- @JsonBackReference er den bakre delen av referansen - den vil bli utelatt fra serialisering.
4. Bruk @JsonIdentityInfo
Nå - la oss se hvordan vi kan hjelpe med serialisering av enheter med toveis forhold ved hjelp @JsonIdentityInfo.
Vi legger til merknad på klassenivå i vår “Bruker”Enhet:
@JsonIdentityInfo (generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") bruker i offentlig klasse {...}
Og til “Punkt”Enhet:
@JsonIdentityInfo (generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") offentlig klasse Element {...}
Tid for testen:
@Test offentlig ugyldighet gittBidirectionRelation_whenUsingJsonIdentityInfo_thenCorrect () kaster JsonProcessingException {Bruker bruker = ny bruker (1, "John"); Vareartikkel = nytt element (2, "bok", bruker); user.addItem (vare); String result = new ObjectMapper (). WriteValueAsString (item); assertThat (resultat, inneholderString ("bok")); assertThat (resultat, inneholderString ("John")); assertThat (resultat, containString ("userItems")); }
Her er produksjonen av serialisering:
{"id": 2, "itemName": "book", "owner": {"id": 1, "name": "John", "userItems": [2]}}
5. Bruk @JsonIgnore
Alternativt kan vi også bruke @JsonIgnore kommentar til ganske enkelt ignorere en av sidene i forholdet, og dermed bryte kjeden.
I det følgende eksemplet - vil vi forhindre den uendelige rekursjonen ved å ignorere “Bruker”Eiendom“userItems”Fra serialisering:
Her er "Bruker”Enhet:
public class User {public int id; offentlig streng navn; @JsonIgnorer offentlig Liste userItems; }
Og her er testen vår:
@Test public void givenBidirectionRelation_whenUsingJsonIgnore_thenCorrect () kaster JsonProcessingException {Bruker bruker = ny bruker (1, "John"); Vareartikkel = nytt element (2, "bok", bruker); user.addItem (vare); String result = new ObjectMapper (). WriteValueAsString (item); assertThat (resultat, inneholderString ("bok")); assertThat (resultat, inneholderString ("John")); assertThat (resultat, ikke (inneholderString ("userItems"))); }
Og her er produksjonen av serialisering:
{"id": 2, "itemName": "book", "owner": {"id": 1, "name": "John"}}
6. Bruk @JsonView
Vi kan også bruke det nyere @JsonView kommentar for å ekskludere den ene siden av forholdet.
I det følgende eksemplet - bruker vi to JSON-visninger - Offentlig og Innvendig hvor Innvendig strekker Offentlig:
offentlig klasse Visninger {offentlig statisk klasse Offentlig {} offentlig statisk klasse Internt utvider Offentlig {}}
Vi inkluderer alle Bruker og Punkt felt i Offentlig Utsikt - bortsett fra Bruker felt userItems som vil bli inkludert i Innvendig Utsikt:
Her er vår enhet “Bruker“:
Public Class User {@JsonView (Views.Public.class) public int id; @JsonView (Views.Public.class) offentlig strengnavn; @JsonView (Views.Internal.class) offentlig liste userItems; }
Og her er vår enhet “Punkt“:
public class Item {@JsonView (Views.Public.class) public int id; @JsonView (Views.Public.class) public String itemName; @JsonView (Views.Public.class) offentlig brukereier; }
Når vi serierer ved hjelp av Offentlig visning, det fungerer riktig - fordi vi ekskluderte userItems fra å bli seriellisert:
@Test offentlig ugyldighet gittBidirectionRelation_whenUsingPublicJsonView_thenCorrect () kaster JsonProcessingException {Bruker bruker = ny bruker (1, "John"); Vareartikkel = nytt element (2, "bok", bruker); user.addItem (vare); String result = new ObjectMapper (). WriterWithView (Views.Public.class) .writeValueAsString (item); assertThat (resultat, inneholderString ("bok")); assertThat (resultat, inneholderString ("John")); assertThat (resultat, ikke (inneholderString ("userItems"))); }
Men hvis vi serierer ved hjelp av en Innvendig utsikt, JsonMappingException kastes fordi alle feltene er inkludert:
@Test (forventet = JsonMappingException.class) offentlig ugyldig givenBidirectionRelation_whenUsingInternalJsonView_thenException () kaster JsonProcessingException {Bruker bruker = ny bruker (1, "John"); Vareartikkel = nytt element (2, "bok", bruker); user.addItem (vare); ny ObjectMapper () .writerWithView (Views.Internal.class) .writeValueAsString (element); }
7. Bruk en Custom Serializer
Neste - la oss se hvordan vi kan serieisere enheter med toveis forhold ved hjelp av en tilpasset serializer.
I det følgende eksemplet - vil vi bruke en tilpasset serielliseringsenhet til å serieisere “Bruker”Eiendom“userItems“:
Her er “Bruker”Enhet:
public class User {public int id; offentlig streng navn; @JsonSerialize (ved hjelp av = CustomListSerializer.class) public List userItems; }
Og her er “CustomListSerializer“:
offentlig klasse CustomListSerializer utvider StdSerializer{public CustomListSerializer () {this (null); } offentlig CustomListSerializer (klasse t) {super (t); } @Override public void serialize (List items, JsonGenerator generator, SerializerProvider provider) kaster IOException, JsonProcessingException {List ids = new ArrayList (); for (Item item: items) {ids.add (item.id); } generator.writeObject (ids); }}
La oss nå teste ut serialisereren og se den riktige typen utdata som produseres:
@Test offentlig ugyldighet gittBidirectionRelation_whenUsingCustomSerializer_thenCorrect () kaster JsonProcessingException {Bruker bruker = ny bruker (1, "John"); Vareartikkel = nytt element (2, "bok", bruker); user.addItem (vare); String result = new ObjectMapper (). WriteValueAsString (item); assertThat (resultat, inneholderString ("bok")); assertThat (resultat, inneholderString ("John")); assertThat (resultat, containString ("userItems")); }
Og den endelige produksjonen av serialiseringen med den tilpassede serialisereren:
{"id": 2, "itemName": "book", "owner": {"id": 1, "name": "John", "userItems": [2]}}
8. Deserialiser med @JsonIdentityInfo
Nå - la oss se hvordan du avserialiserer enheter med toveis forhold ved hjelp av @JsonIdentityInfo.
Her er "Bruker”Enhet:
@JsonIdentityInfo (generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") bruker i offentlig klasse {...}
Og "Punkt”Enhet:
@JsonIdentityInfo (generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") offentlig klasse Element {...}
La oss nå skrive en rask test - startende med noen manuelle JSON-data vi vil analysere og avslutte med riktig konstruert enhet:
@Test offentlig ugyldighet gittBidirectionRelation_whenDeserializingWithIdentity_thenCorrect () kaster JsonProcessingException, IOException {String json = "{\" id \ ": 2, \" itemName \ ": \" book \ ", \" eier \ ": {\" id \ ": 1, \ "name \": \ "John \", \ "userItems \": [2]}} "; ItemWithIdentity item = new ObjectMapper (). ReaderFor (ItemWithIdentity.class) .readValue (json); assertEquals (2, item.id); assertEquals ("bok", item.itemName); assertEquals ("John", item.owner.name); }
9. Bruk Custom Deserializer
Til slutt, la oss deserialisere enhetene med toveis forhold med en tilpasset deserializer.
I det følgende eksemplet - vil vi bruke tilpasset deserializer til å analysere “Bruker”Eiendom“userItems“:
Her er “Bruker”Enhet:
public class User {public int id; offentlig streng navn; @JsonDeserialize (ved hjelp av = CustomListDeserializer.class) public List userItems; }
Og her er vår “CustomListDeserializer“:
offentlig klasse CustomListDeserializer utvider StdDeserializer{public CustomListDeserializer () {this (null); } offentlig CustomListDeserializer (klasse vc) {super (vc); } @Override public List deserialize (JsonParser jsonparser, DeserializationContext context) kaster IOException, JsonProcessingException {return new ArrayList (); }}
Og den enkle testen:
@Test offentlig ugyldighet gittBidirectionRelation_whenUsingCustomDeserializer_thenCorrect () kaster JsonProcessingException, IOException {String json = "{\" id \ ": 2, \" itemName \ ": \" book \ ", \" eier \ ": {\" id \ ": 1, \ "name \": \ "John \", \ "userItems \": [2]}} "; Element element = ny ObjectMapper (). ReaderFor (Item.class) .readValue (json); assertEquals (2, item.id); assertEquals ("book", item.itemName); assertEquals ("John", item.owner.name); }
10. Konklusjon
I denne opplæringen illustrerte vi hvordan du serialiserer / deserialiserer enheter med toveis relasjoner ved hjelp av Jackson.
Implementeringen av alle disse eksemplene og kodebiter kan du finne i GitHub-prosjektet vårt - dette er et Maven-basert prosjekt, så det skal være enkelt å importere og kjøre som det er.