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.