Jackson - Kétirányú kapcsolatok

1. Áttekintés

Ebben az oktatóanyagban áttekintjük a kezelés legjobb módjait kétirányú kapcsolatok Jacksonban.

Megbeszéljük a Jackson JSON végtelen rekurziós problémáját, majd - meglátjuk, hogyan lehet kétirányú kapcsolatokkal rendelkező entitásokat sorosítani és végül - deszerializáljuk őket.

2. Végtelen rekurzió

Először - vessünk egy pillantást a Jackson végtelen rekurziós problémájára. A következő példában két entitásunk van:Felhasználó”És„Tétel" - val vel egyszerű egy a sokhoz viszony:

A "Felhasználó”Entitás:

public class Felhasználó {public int id; public String név; public List userItems; }

A "Tétel”Entitás:

public class Item {public int id; public String itemName; nyilvános Felhasználótulajdonos; }

Amikor megpróbáljuk sorosítani aTétel“, Jackson dob egy JsonMappingException kivétel:

@Test (várható = JsonMappingException.class) public void givenBidirectionRelation_whenSerializing_thenException () dobja a JsonProcessingException {User user = new User (1, "John"); Tétel elem = új Tétel (2, "könyv", felhasználó); user.addItem (elem); új ObjectMapper (). writeValueAsString (elem); }

A teljes kivétel az:

com.fasterxml.jackson.databind.JsonMappingException: Végtelen rekurzió (StackOverflowError) (hivatkozási láncon keresztül: org.baeldung.jackson.bidirection.Item ["tulajdonos"] -> org.baeldung.jackson.bidirection.User ["userItems"] -> java.util.ArrayList [0] -> org.baeldung.jackson.bidirection.Item ["tulajdonos"] ->… ..

Lássuk a következő szakaszok során - hogyan lehet megoldani ezt a problémát.

3. Használja @JsonManagedReference, @JsonBackReference

Először jegyezzük fel a kapcsolatot @JsonManagedReference, @JsonBackReference hogy Jackson jobban kezelhesse a kapcsolatot:

Itt van aFelhasználó”Entitás:

public class Felhasználó {public int id; public String név; @JsonBackReference public List userItems; }

És a "Tétel“:

public class Item {public int id; public String itemName; @JsonManagedReference nyilvános felhasználói tulajdonos; }

Próbáljuk ki most az új entitásokat:

@Test public void givenBidirectionRelation_whenUsingJacksonReferenceAnnotation_thenCorrect () dobja JsonProcessingException {Felhasználó felhasználó = új felhasználó (1, "John"); Tétel elem = új Tétel (2, "könyv", felhasználó); user.addItem (elem); Karakterlánc eredménye = új ObjectMapper (). WriteValueAsString (elem); assertThat (eredmény, tartalmazzaString ("könyv")); assertThat (eredmény, tartalmazzaString ("John")); assertThat (eredmény, nem (tartalmazzaString ("userItems"))); }

Itt van a sorozatosítás eredménye:

{"id": 2, "itemName": "book", "owner": {"id": 1, "name": "John"}}

Vegye figyelembe, hogy:

  • @JsonManagedReference a referencia elülső része - amely normálisan sorosodik.
  • @JsonBackReference a referencia hátsó része - a sorosításból kihagyják.

4. Használja @JsonIdentityInfo

Most - nézzük meg, hogyan segíthetünk a kétirányú kapcsolattal rendelkező entitások sorosításában @JsonIdentityInfo.

Hozzáadjuk az osztály szintű kommentárokat aFelhasználó”Entitás:

@JsonIdentityInfo (generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") public class Felhasználó {...}

És a „Tétel”Entitás:

@JsonIdentityInfo (generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") public class Item {...}

A teszt ideje:

@Test public void givenBidirectionRelation_whenUsingJsonIdentityInfo_thenCorrect () dobja JsonProcessingException {Felhasználó felhasználó = új felhasználó (1, "John"); Tétel elem = új Tétel (2, "könyv", felhasználó); user.addItem (elem); Karakterlánc eredménye = új ObjectMapper (). WriteValueAsString (elem); assertThat (eredmény, tartalmazzaString ("könyv")); assertThat (eredmény, tartalmazzaString ("John")); assertThat (eredmény, tartalmazzaString ("userItems")); }

Itt van a sorozatosítás eredménye:

{"id": 2, "itemName": "book", "owner": {"id": 1, "name": "John", "userItems": [2]}}

5. Használja @JsonIgnore

Alternatív megoldásként használhatjuk a @JsonIgnore annotáció egyszerűen figyelmen kívül hagyja a kapcsolat egyik oldalát, így megtörve a láncot.

A következő példában - megakadályozzuk a végtelen rekurziót, ha figyelmen kívül hagyjuk aFelhasználó" ingatlan "userItems”A sorosítástól:

Itt van "Felhasználó”Entitás:

public class Felhasználó {public int id; public String név; @JsonIgnore public List userItems; }

És itt van a tesztünk:

@Test public void givenBidirectionRelation_whenUsingJsonIgnore_thenCorrect () dobja a JsonProcessingException {felhasználói felhasználót = új felhasználót (1, "John"); Tétel elem = új Tétel (2, "könyv", felhasználó); user.addItem (elem); Karakterlánc eredménye = új ObjectMapper (). WriteValueAsString (elem); assertThat (eredmény, tartalmazzaString ("könyv")); assertThat (eredmény, tartalmazzaString ("John")); assertThat (eredmény, nem (tartalmazzaString ("userItems"))); }

És itt van a sorosítás eredménye:

{"id": 2, "itemName": "book", "owner": {"id": 1, "name": "John"}}

6. Használja @JsonView

Használhatjuk az újabbat is @JsonView kommentár a kapcsolat egyik oldalának kizárására.

A következő példában - használjuk két JSON nézet - Nyilvános és Belső hol Belső kiterjed Nyilvános:

public class Views {public static class Public {} public static class Internal extens Public {}}

Mindet belefoglaljuk Felhasználó és Tétel mezők a Nyilvános Kilátás - kivéve a Felhasználó terület userItems amelyek bekerülnek a Belső Kilátás:

Itt van az entitásunkFelhasználó“:

public class Felhasználó {@JsonView (Views.Public.class) public int id; @JsonView (Views.Public.class) nyilvános karakterlánc neve; @JsonView (Views.Internal.class) public list userItems; }

És itt van az entitásunkTétel“:

public class Item {@JsonView (Views.Public.class) public int id; @JsonView (Views.Public.class) public String itemName; @JsonView (Views.Public.class) nyilvános Felhasználótulajdonos; }

Amikor sorosítunk a Nyilvános nézetben megfelelően működik - mert kizártuk userItems a sorosítástól:

@Test public void givenBidirectionRelation_whenUsingPublicJsonView_thenCorrect () dobja a JsonProcessingException {felhasználói felhasználót = új felhasználót (1, "John"); Tétel elem = új Tétel (2, "könyv", felhasználó); user.addItem (elem); Karakterlánc eredménye = új ObjectMapper (). ÍróWithView (Views.Public.class) .writeValueAsString (elem); assertThat (eredmény, tartalmazzaString ("könyv")); assertThat (eredmény, tartalmazzaString ("John")); assertThat (eredmény, nem (tartalmazzaString ("userItems"))); }

De ha sorosítunk egy Belső Kilátás, JsonMappingException dobásra kerül, mert az összes mező benne van:

@Test (várható = JsonMappingException.class) public void givenBidirectionRelation_whenUsingInternalJsonView_thenException () dobja a JsonProcessingException {User user = new User (1, "John"); Tétel elem = új Tétel (2, "könyv", felhasználó); user.addItem (elem); új ObjectMapper () .writerWithView (Views.Internal.class) .writeValueAsString (elem); }

7. Használjon Custom Serializer-t

Ezután - nézzük meg, hogyan lehet kétirányú kapcsolattal rendelkező entitásokat sorosítani egy egyedi sorosító segítségével.

A következő példában - egy egyedi sorosítót fogunk használni aFelhasználó" ingatlan "userItems“:

Itt van aFelhasználó”Entitás:

public class Felhasználó {public int id; public String név; @JsonSerialize (using = CustomListSerializer.class) public List userItems; }

És itt van a „CustomListSerializer“:

a public class CustomListSerializer kiterjeszti az StdSerializer alkalmazást{public CustomListSerializer () {this (null); } public CustomListSerializer (t osztály) {super (t); } @Orride public void serialize (Lista elemek, JsonGenerator generátor, SerializerProvider szolgáltató) dobja az IOException, JsonProcessingException {List ids = new ArrayList (); for (Cikk elem: elemek) {ids.add (item.id); } generator.writeObject (ids); }}

Próbáljuk ki a sorosítót, és nézzük meg a megfelelő típusú kimenetet:

@Test public void givenBidirectionRelation_whenUsingCustomSerializer_thenCorrect () dobja a JsonProcessingException {felhasználói felhasználót = új felhasználót (1, "John"); Tétel elem = új Tétel (2, "könyv", felhasználó); user.addItem (elem); Karakterlánc eredménye = új ObjectMapper (). WriteValueAsString (elem); assertThat (eredmény, tartalmazzaString ("könyv")); assertThat (eredmény, tartalmazzaString ("John")); assertThat (eredmény, tartalmazzaString ("userItems")); }

És a végső eredmény az egyedi szerializálóval történő sorosítás:

{"id": 2, "itemName": "book", "owner": {"id": 1, "name": "John", "userItems": [2]}}

8. Deserialize With @JsonIdentityInfo

Most - nézzük meg, hogyan lehet deserializálni az entitásokat kétirányú kapcsolattal @JsonIdentityInfo.

Itt van "Felhasználó”Entitás:

@JsonIdentityInfo (generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") public class Felhasználó {...}

És a "Tétel”Entitás:

@JsonIdentityInfo (generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") public class Item {...}

Írjunk most egy gyors tesztet - néhány kézi JSON-adattal kezdve, amelyeket elemezni akarunk, és befejezzük a helyesen felépített entitást:

@Test public void givenBidirectionRelation_whenDeserializingWithIdentity_thenCorrect () dobja a JsonProcessingException, IOException {String json = "{\" id \ ": 2, \" itemName \ ": \" book \ ", \" owner \ ": {\" id \ ": 1, \ "név \": \ "John \", \ "userItems \": [2]}} "; ItemWithIdentity item = new ObjectMapper (). ReaderFor (ItemWithIdentity.class) .readValue (json); assertEquals (2, item.id); assertEquals ("könyv", item.itemName); assertEquals ("John", item.owner.name); }

9. Használjon Custom Deserializer-t

Végül deserializáljuk a kétirányú kapcsolattal rendelkező entitásokat egy egyedi deserializer segítségével.

A következő példában - egyéni deserializer-t használunk aFelhasználó" ingatlan "userItems“:

Itt vanFelhasználó”Entitás:

public class Felhasználó {public int id; public String név; @JsonDeserialize (using = CustomListDeserializer.class) public List userItems; }

És itt van a miCustomListDeserializer“:

public class CustomListDeserializer kiterjeszti az StdDeserializer alkalmazást{public CustomListDeserializer () {this (null); } public CustomListDeserializer (vc osztály) {super (vc); } @Override public List deserialize (JsonParser jsonparser, DeserializationContext context) dobja az IOException, JsonProcessingException {return new ArrayList (); }}

És az egyszerű teszt:

@Test public void givenBidirectionRelation_whenUsingCustomDeserializer_thenCorrect () dobja a JsonProcessingException, IOException {String json = "{\" id \ ": 2, \" itemName \ ": \" book \ ", \" owner \ ": {\" id \ ": 1, \ "név \": \ "John \", \ "userItems \": [2]}} "; Elem elem = új ObjectMapper (). ReaderFor (Elem.osztály) .readValue (json); assertEquals (2, item.id); assertEquals ("könyv", item.itemName); assertEquals ("John", item.owner.name); }

10. Következtetés

Ebben az oktatóanyagban bemutattuk, hogyan lehet kétirányú kapcsolatokkal rendelkező entitásokat sorosítani / dezerializálni Jackson segítségével.

Mindezen példák és kódrészletek megvalósítása megtalálható a GitHub projektünkben - ez egy Maven-alapú projekt, ezért könnyen importálhatónak és futtathatónak kell lennie.