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.


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