Jackson - Dvosmjerni odnosi

1. Pregled

U ovom ćemo uputstvu razmotriti najbolje načine rješavanja dvosmjerni odnosi u Jacksonu.

Razmotrit ćemo problem beskonačne rekurzije Jacksona JSON-a, zatim - vidjet ćemo kako serializirati entitete s dvosmjernim odnosima i na kraju - deserializirati ih.

2. Beskonačna rekurzija

Prvo - pogledajmo Jacksonov problem beskonačne rekurzije. U sljedećem primjeru imamo dva entiteta - “Korisnik"I"Artikal”- sa jednostavan odnos jedan prema mnogima:

"Korisnik”Entitet:

javni razred korisnika {javni int id; naziv javnog niza; javni popis userItems; }

"Artikal”Entitet:

predmet javne klase {public int id; public String itemName; javni korisnik; }

Kada pokušamo serializirati instancu „Artikal“, Jackson će baciti JsonMappingException iznimka:

@Test (očekuje se = JsonMappingException.class) javna praznina givenBidirectionRelation_whenSerializing_thenException () baca JsonProcessingException {Korisnik = novi korisnik (1, "John"); Stavka predmeta = nova stavka (2, "knjiga", korisnik); user.addItem (stavka); novi ObjectMapper (). writeValueAsString (stavka); }

The puna iznimka je:

com.fasterxml.jackson.databind.JsonMappingException: Beskonačna rekurzija (StackOverflowError) (kroz referentni lanac: org.baeldung.jackson.bidirection.Item ["owner"] -> org.baeldung.jackson.bidirection.User ["userItems"] -> java.util.ArrayList [0] -> org.baeldung.jackson.bidirection.Item ["vlasnik"] -> ... ..

Da vidimo, tijekom sljedećih nekoliko odjeljaka - kako riješiti ovaj problem.

3. Koristite @JsonManagedReference, @JsonBackReference

Prvo, zabilježimo odnos s @JsonManagedReference, @JsonBackReference kako bi Jacksonu omogućio da bolje riješi odnos:

Evo "Korisnik”Entitet:

javni razred korisnika {javni int id; naziv javnog niza; @JsonBackReference javni popis userItems; }

I „Artikal“:

predmet javne klase {public int id; public String itemName; @JsonManagedReference javni korisnik; }

Isprobajmo sada nove entitete:

@Test javna praznina givenBidirectionRelation_whenUsingJacksonReferenceAnnotation_thenCorrect () baca JsonProcessingException {User user = novi korisnik (1, "John"); Stavka predmeta = nova stavka (2, "knjiga", korisnik); user.addItem (stavka); Rezultat niza = novi ObjectMapper (). WriteValueAsString (stavka); assertThat (rezultat, sadržiString ("knjiga")); assertThat (rezultat, sadržiString ("Ivan")); assertThat (rezultat, ne (sadržiString ("userItems"))); }

Evo rezultata serializacije:

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

Imajte na umu da:

  • @JsonManagedReference je prednji dio reference - onaj koji se normalno serializira.
  • @JsonBackReference zadnji je dio reference - izostavit će se iz serializacije.

4. Koristite @JsonIdentityInfo

Sada - pogledajmo kako pomoći u serializaciji entiteta s dvosmjernim odnosom @JsonIdentityInfo.

Dodajemo napomenu na razini predavanja u naš “Korisnik”Entitet:

@JsonIdentityInfo (generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") Korisnik javne klase {...}

I „Artikal”Entitet:

@JsonIdentityInfo (generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") Predmet javne klase {...}

Vrijeme za test:

@Test javna praznina givenBidirectionRelation_whenUsingJsonIdentityInfo_thenCorrect () baca JsonProcessingException {Korisnik = novi korisnik (1, "John"); Stavka predmeta = nova stavka (2, "knjiga", korisnik); user.addItem (stavka); Rezultat niza = novi ObjectMapper (). WriteValueAsString (stavka); assertThat (rezultat, sadržiString ("knjiga")); assertThat (rezultat, sadržiString ("Ivan")); assertThat (rezultat, sadržiString ("userItems")); }

Evo rezultata serializacije:

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

5. Koristite @JsonIgnore

Alternativno, možemo koristiti i @JsonIgnore anotacija na jednostavno zanemariti jednu od strana odnosa, prekidajući tako lanac.

U sljedećem primjeru - spriječit ćemo beskonačnu rekurziju ignoriranjem "Korisnik"Svojstvo"Artikli korisnika”Iz serializacije:

Ovdje je "Korisnik”Entitet:

javni razred korisnika {javni int id; naziv javnog niza; @JsonIgnore javni popis userItems; }

I evo našeg testa:

@Test javna praznina givenBidirectionRelation_whenUsingJsonIgnore_thenCorrect () baca JsonProcessingException {Korisnik = novi korisnik (1, "John"); Stavka predmeta = nova stavka (2, "knjiga", korisnik); user.addItem (stavka); Rezultat niza = novi ObjectMapper (). WriteValueAsString (stavka); assertThat (rezultat, sadržiString ("knjiga")); assertThat (rezultat, sadržiString ("Ivan")); assertThat (rezultat, ne (sadržiString ("userItems"))); }

I evo rezultata serializacije:

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

6. Koristite @JsonView

Možemo koristiti i noviju @JsonView napomena za isključivanje jedne strane odnosa.

U sljedećem primjeru - koristimo dva JSON prikaza - Javnost i Interno gdje Interno proteže se Javnost:

prikazi javne klase {public static class Public {} public static class Internal extends Public {}}

Uključit ćemo sve Korisnik i Artikal polja u Javnost Pogled - osim Korisnik polje Artikli korisnika koji će biti uključeni u Interno Pogled:

Evo našeg entiteta “Korisnik“:

javni razred Korisnik {@JsonView (Views.Public.class) javni int id; @JsonView (Views.Public.class) naziv javnog niza; @JsonView (Views.Internal.class) javni popis userItems; }

I ovdje je naš entitet “Artikal“:

stavka javne klase {@JsonView (Views.Public.class) public int id; @JsonView (Views.Public.class) javni String itemName; @JsonView (Views.Public.class) javni korisnik; }

Kad se serializiramo pomoću Javnost pogled, radi ispravno - jer smo isključili Artikli korisnika od serializacije:

@Test javna praznina givenBidirectionRelation_whenUsingPublicJsonView_thenCorrect () baca JsonProcessingException {Korisnik = novi korisnik (1, "John"); Stavka predmeta = nova stavka (2, "knjiga", korisnik); user.addItem (stavka); Rezultat niza = novi ObjectMapper (). WriterWithView (Views.Public.class) .writeValueAsString (stavka); assertThat (rezultat, sadržiString ("knjiga")); assertThat (rezultat, sadržiString ("Ivan")); assertThat (rezultat, ne (sadržiString ("userItems"))); }

Ali ako serializiramo pomoću Interno pogled, JsonMappingException bačen je jer su sva polja uključena:

@Test (očekuje se = JsonMappingException.class) javna praznina givenBidirectionRelation_whenUsingInternalJsonView_thenException () baca JsonProcessingException {Korisnik = novi korisnik (1, "John"); Stavka predmeta = nova stavka (2, "knjiga", korisnik); user.addItem (stavka); novi ObjectMapper () .writerWithView (Views.Internal.class) .writeValueAsString (stavka); }

7. Upotrijebite prilagođeni serilizator

Dalje - pogledajmo kako serializirati entitete s dvosmjernim odnosom pomoću prilagođenog serializatora.

U sljedećem primjeru - koristit ćemo prilagođeni serializator za serializaciju "Korisnik"Svojstvo"Artikli korisnika“:

Evo "Korisnik”Entitet:

javni razred korisnika {javni int id; naziv javnog niza; @JsonSerialize (koristeći = CustomListSerializer.class) javni popis userItems; }

A ovdje je „CustomListSerializer“:

javna klasa CustomListSerializer proširuje StdSerializer{public CustomListSerializer () {this (null); } javni CustomListSerializer (klasa t) {super (t); } @Override public void serialize (Stavke popisa, generator JsonGenerator, dobavljač SerializerProvider) baca IOException, JsonProcessingException {Id popisa = novi ArrayList (); za (Stavka artikla: stavke) {ids.add (item.id); } generator.writeObject (ids); }}

Ajmo sada testirati serializator i vidjeti pravu vrstu rezultata koji se proizvode:

@Test javna praznina givenBidirectionRelation_whenUsingCustomSerializer_thenCorrect () baca JsonProcessingException {Korisnik = novi korisnik (1, "John"); Stavka predmeta = nova stavka (2, "knjiga", korisnik); user.addItem (stavka); Rezultat niza = novi ObjectMapper (). WriteValueAsString (stavka); assertThat (rezultat, sadržiString ("knjiga")); assertThat (rezultat, sadržiString ("Ivan")); assertThat (rezultat, sadržiString ("userItems")); }

I konačni izlaz serializacije s prilagođenim serializatorom:

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

8. Deserijalizirajte s @JsonIdentityInfo

Sada - pogledajmo kako deserijalizirati entitete s dvosmjernim odnosom pomoću @JsonIdentityInfo.

Ovdje je "Korisnik”Entitet:

@JsonIdentityInfo (generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") Korisnik javne klase {...}

I „Artikal”Entitet:

@JsonIdentityInfo (generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") Predmet javne klase {...}

Napišimo sada brzi test - počevši od nekih ručnih JSON podataka koje želimo raščlaniti i završavajući ispravno konstruiranim entitetom:

@Test javna praznina givenBidirectionRelation_whenDeserializingWithIdentity_thenCorrect () baca JsonProcessingException, IOException {String json = "{\" id \ ": 2, \" itemName \ ": \" book \ ", \" owner \ ": {\" id \ "id \" 1, \ "name \": \ "John \", \ "userItems \": [2]}} "; ItemWithIdentity item = new ObjectMapper (). ReaderFor (ItemWithIdentity.class) .readValue (json); assertEquals (2, item.id); assertEquals ("knjiga", item.itemName); assertEquals ("John", item.owner.name); }

9. Koristite prilagođeni deserijalizator

Na kraju, hajde deserializirajte entitete s dvosmjernim odnosom koristeći prilagođeni deserijalizator.

U sljedećem primjeru - koristit ćemo prilagođeni deserijalizator za raščlanjivanje "Korisnik"Svojstvo"Artikli korisnika“:

Evo “Korisnik”Entitet:

javni razred korisnika {javni int id; naziv javnog niza; @JsonDeserialize (koristeći = CustomListDeserializer.class) javni popis userItems; }

I evo našeg “CustomListDeserializer“:

javna klasa CustomListDeserializer proširuje StdDeserializer{public CustomListDeserializer () {this (null); } javni CustomListDeserializer (klasa vc) {super (vc); } @Override javni popis deserializira (JsonParser jsonparser, DeserializationContext context) baca IOException, JsonProcessingException {return new ArrayList (); }}

I jednostavan test:

@Test javna praznina givenBidirectionRelation_whenUsingCustomDeserializer_thenCorrect () baca JsonProcessingException, IOException {String json = "{\" id \ ": 2, \" itemName \ ": \" book \ ", \" owner \ ": {\" id \ ": 1, \ "name \": \ "John \", \ "userItems \": [2]}} "; Stavka stavke = novi ObjectMapper (). ReaderFor (Item.class) .readValue (json); assertEquals (2, item.id); assertEquals ("knjiga", item.itemName); assertEquals ("John", item.owner.name); }

10. Zaključak

U ovom uputstvu ilustrirali smo kako serializirati / deserializirati entitete s dvosmjernim odnosima pomoću Jacksona.

Implementacija svih ovih primjera i isječaka koda možete pronaći u našem GitHub projektu - ovo je projekt zasnovan na Mavenu, pa bi ga trebalo biti lako uvesti i pokrenuti kakav jest.


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