Jackson - obojsmerné vzťahy
1. Prehľad
V tomto tutoriáli si ukážeme najlepšie spôsoby riešenia obojsmerné vzťahy v Jackson.
Budeme diskutovať o probléme nekonečnej rekurzie Jacksona JSONA, potom - uvidíme, ako serializovať entity s obojsmernými vzťahmi a nakoniec - ich deserializujeme.
2. Nekonečná rekurzia
Po prvé - poďme sa pozrieť na Jacksonov problém nekonečnej rekurzie. V nasledujúcom príklade máme dve entity - „Používateľ“A„Položka”- s jednoduchý vzťah jeden na viacerých:
„Používateľ”Entita:
public class User {public int id; verejné meno reťazca; public List userItems; }
„Položka”Entita:
public class Item {public int id; public String itemName; verejný vlastník používateľa; }
Keď sa pokúsime serializovať inštanciu „Položka“, Jackson hodí JsonMappingException výnimka:
@Test (očakáva sa = JsonMappingException.class) public void givenBidirectionRelation_whenSerializing_thenException () hodí JsonProcessingException {User user = new User (1, "John"); Položka položka = nová položka (2, „kniha“, používateľ); user.addItem (položka); new ObjectMapper (). writeValueAsString (item); }
The úplná výnimka je:
com.fasterxml.jackson.databind.JsonMappingException: Nekonečná rekurzia (StackOverflowError) (prostredníctvom referenčného reťazca: org.baeldung.jackson.bidirection.Item ["vlastník"] -> org.baeldung.jackson.bidirection.User ["userItems"] -> java.util.ArrayList [0] -> org.baeldung.jackson.bidirection.Item ["vlastník"] ->… ..
Pozrime sa, v priebehu niekoľkých nasledujúcich častí - ako vyriešiť tento problém.
3. Použite @JsonManagedReference, @JsonBackReference
Najskôr anotujme vzťah s @JsonManagedReference, @JsonBackReference aby Jackson mohol lepšie zvládnuť vzťah:
Tu je „Používateľ”Entita:
public class User {public int id; verejné meno reťazca; @JsonBackReference verejný zoznam userItems; }
A „Položka“:
public class Item {public int id; public String itemName; @JsonManagedReference public Vlastník používateľa; }
Poďme teraz otestovať nové entity:
@Test public void givenBidirectionRelation_whenUsingJacksonReferenceAnnotation_thenCorrect () hodí JsonProcessingException {User user = new User (1, "John"); Položka položka = nová položka (2, „kniha“, používateľ); user.addItem (položka); Výsledok reťazca = nový ObjectMapper (). WriteValueAsString (položka); assertThat (result, containsString ("kniha")); assertThat (result, containsString ("John")); assertThat (výsledok, nie (containsString ("userItems"))); }
Tu je výstup serializácie:
{"id": 2, "itemName": "book", "owner": {"id": 1, "name": "John"}}
Poznač si to:
- @JsonManagedReference je predná časť referencie - tá, ktorá sa normálne serializuje.
- @JsonBackReference je zadná časť referencie - bude vynechaná zo serializácie.
4. Použite @JsonIdentityInfo
Teraz - pozrime sa, ako pomôcť pri serializácii entít s využitím obojsmerného vzťahu @JsonIdentityInfo.
Pridávame anotáciu na úrovni triedy k nášmu „Používateľ”Entita:
@JsonIdentityInfo (generátor = ObjectIdGenerators.PropertyGenerator.class, property = "id") verejná trieda Používateľ {...}
A k „Položka”Entita:
@JsonIdentityInfo (generátor = ObjectIdGenerators.PropertyGenerator.class, property = "id") verejná trieda Položka {...}
Čas na vykonanie testu:
@Test public void givenBidirectionRelation_whenUsingJsonIdentityInfo_thenCorrect () vyvolá JsonProcessingException {User user = new User (1, "John"); Položka položka = nová položka (2, „kniha“, používateľ); user.addItem (položka); Výsledok reťazca = nový ObjectMapper (). WriteValueAsString (položka); assertThat (result, containsString ("kniha")); assertThat (result, containsString ("John")); assertThat (result, containsString ("userItems")); }
Tu je výstup serializácie:
{"id": 2, "itemName": "book", "owner": {"id": 1, "name": "John", "userItems": [2]}}
5. Použite @JsonIgnore
Prípadne môžeme použiť aj @JsonIgnore anotácia jednoducho ignorovať jednu zo strán vzťahu, čím sa pretrhla reťaz.
V nasledujúcom príklade - zabránime nekonečnej rekurzii ignorovaním „Používateľ" nehnuteľnosť "userItems”Zo serializácie:
Tu je „Používateľ”Entita:
public class User {public int id; verejné meno reťazca; @JsonIgnorovať verejný zoznam userItems; }
A tu je náš test:
@Test public void givenBidirectionRelation_whenUsingJsonIgnore_thenCorrect () vyvolá JsonProcessingException {User user = new User (1, "John"); Položka položka = nová položka (2, „kniha“, používateľ); user.addItem (položka); Výsledok reťazca = nový ObjectMapper (). WriteValueAsString (položka); assertThat (result, containsString ("kniha")); assertThat (result, containsString ("John")); assertThat (výsledok, nie (containsString ("userItems"))); }
A tu je výstup serializácie:
{"id": 2, "itemName": "book", "owner": {"id": 1, "name": "John"}}
6. Použite @JsonView
Môžeme použiť aj novšie @JsonView anotácia na vylúčenie jednej strany vzťahu.
V nasledujúcom príklade - používame dve zobrazenia JSON - Verejné a Interné kde Interné predlžuje Verejné:
public class Views {public static class Public {} public static class Internal extends Public {}}
Zahrnieme všetky Používateľ a Položka polia v Verejné Vyhliadka - okrem Používateľ lúka userItems ktoré budú zahrnuté do Interné Vyhliadka:
Tu je naša entita “Používateľ“:
public class User {@JsonView (Views.Public.class) public int id; @JsonView (Views.Public.class) public Názov reťazca; @JsonView (Views.Internal.class) public List userItems; }
A tu je naša entita “Položka“:
public class Item {@JsonView (Views.Public.class) public int id; @JsonView (Views.Public.class) public String itemName; @JsonView (Views.Public.class) verejný vlastník používateľa; }
Keď serializujeme pomocou Verejné pohľad funguje správne - pretože sme vylúčili userItems zo serializácie:
@Test public void givenBidirectionRelation_whenUsingPublicJsonView_thenCorrect () hodí JsonProcessingException {User user = new User (1, "John"); Položka položka = nová položka (2, „kniha“, používateľ); user.addItem (položka); Výsledok reťazca = nový ObjectMapper (). WriterWithView (Views.Public.class) .writeValueAsString (položka); assertThat (result, containsString ("kniha")); assertThat (result, containsString ("John")); assertThat (výsledok, nie (containsString ("userItems"))); }
Ale ak budeme serializovať pomocou Interné vyhliadka, JsonMappingException je vyhodené, pretože sú zahrnuté všetky polia:
@Test (očakáva sa = JsonMappingException.class) public void givenBidirectionRelation_whenUsingInternalJsonView_thenException () vyvolá JsonProcessingException {User user = new User (1, "John"); Položka položka = nová položka (2, „kniha“, používateľ); user.addItem (položka); nový ObjectMapper () .writerWithView (Views.Internal.class) .writeValueAsString (položka); }
7. Použite vlastný serializátor
Ďalej - pozrime sa, ako serializovať entity s obojsmerným vzťahom pomocou vlastného serializátora.
V nasledujúcom príklade - na serializáciu použijeme vlastný serializátorPoužívateľ" nehnuteľnosť "userItems“:
Tu je „Používateľ”Entita:
public class User {public int id; verejné meno reťazca; @JsonSerialize (using = CustomListSerializer.class) public List userItems; }
A tu je „CustomListSerializer“:
verejná trieda CustomListSerializer rozširuje StdSerializer{public CustomListSerializer () {this (null); } public CustomListSerializer (trieda t) {super (t); } @Override public void serialize (Zoznam položiek, generátor JsonGenerator, poskytovateľ SerializerProvider) hodí IOException, JsonProcessingException {List ids = new ArrayList (); pre (Položka položky: položky) {ids.add (item.id); } generator.writeObject (ids); }}
Poďme teraz otestovať serializátor a uvidíme, aký typ výstupu sa produkuje:
@ Test public void givenBidirectionRelation_whenUsingCustomSerializer_thenCorrect () hodí JsonProcessingException {User user = new User (1, "John"); Položka položka = nová položka (2, „kniha“, používateľ); user.addItem (položka); Výsledok reťazca = nový ObjectMapper (). WriteValueAsString (položka); assertThat (result, containsString ("kniha")); assertThat (result, containsString ("John")); assertThat (result, containsString ("userItems")); }
A konečný výstup serializácie pomocou vlastného serializátora:
{"id": 2, "itemName": "book", "owner": {"id": 1, "name": "John", "userItems": [2]}}
8. Deserializovať s @JsonIdentityInfo
Teraz - pozrime sa, ako deserializovať entity s obojsmerným vzťahom pomocou @JsonIdentityInfo.
Tu je "Používateľ”Entita:
@JsonIdentityInfo (generátor = ObjectIdGenerators.PropertyGenerator.class, property = "id") verejná trieda Používateľ {...}
A „Položka”Entita:
@JsonIdentityInfo (generátor = ObjectIdGenerators.PropertyGenerator.class, property = "id") verejná trieda Položka {...}
Poďme teraz napísať rýchly test - počnúc niekoľkými manuálnymi údajmi JSON, ktoré chceme analyzovať, a končíme správne zostavenou entitou:
@Test public void givenBidirectionRelation_whenDeserializingWithIdentity_thenCorrect () vyvolá JsonProcessingException, IOException {String json = "{\" id \ ": 2, \" itemName \ ": \" book \ ", \" owner \ ": {\" id \ ": 1, \ "name \": \ "John \", \ "userItems \": [2]}} "; ItemWithIdentity item = nový ObjectMapper (). ReaderFor (ItemWithIdentity.class) .readValue (json); assertEquals (2, item.id); assertEquals ("kniha", item.itemName); assertEquals ("John", item.owner.name); }
9. Použite vlastný deserializátor
Na záver poďme rekonštruovať entity s obojsmerným vzťahom pomocou vlastného deserializátora.
V nasledujúcom príklade - použijeme vlastný deserializátor na analýzuPoužívateľ" nehnuteľnosť "userItems“:
Tu je „Používateľ”Entita:
public class User {public int id; verejné meno reťazca; @JsonDeserialize (using = CustomListDeserializer.class) public List userItems; }
A tu je náš „CustomListDeserializer“:
verejná trieda CustomListDeserializer rozširuje StdDeserializer{public CustomListDeserializer () {this (null); } public CustomListDeserializer (trieda vc) {super (vc); } @Override public List deserialize (JsonParser jsonparser, DeserializationContext context) hodí IOException, JsonProcessingException {return new ArrayList (); }}
A jednoduchý test:
@Test public void givenBidirectionRelation_whenUsingCustomDeserializer_thenCorrect () hodí JsonProcessingException, IOException {String json = "{\" id \ ": 2, \" itemName \ ": \" book \ ", \" vlastník \ ": {\" id \ ": 1, \ "name \": \ "John \", \ "userItems \": [2]}} "; Položka item = new ObjectMapper (). ReaderFor (Item.class) .readValue (json); assertEquals (2, item.id); assertEquals ("kniha", item.itemName); assertEquals ("John", item.owner.name); }
10. Záver
V tomto tutoriáli sme ilustrovali, ako serializovať / deserializovať entity s obojsmernými vzťahmi pomocou Jacksona.
Implementácia všetkých týchto príkladov a útržkov kódu nájdete v našom projekte GitHub - toto je projekt založený na Maven, takže by malo byť ľahké ho importovať a spustiť tak, ako je.