Nasljeđivanje s Jacksonom

1. Pregled

U ovom ćemo članku pogledati rad s hijerarhijama klasa u Jacksonu.

Dva su tipična slučaja upotrebe uključivanje metapodataka podtipa i zanemarivanje svojstava naslijeđenih od superrazreda. Opisat ćemo ta dva scenarija i nekoliko okolnosti u kojima je potreban poseban tretman podtipova.

2. Uključivanje podataka o podtipu

Postoje dva načina za dodavanje informacija o tipu prilikom serializacije i deserializacije podatkovnih objekata, a to su globalno zadano tipkanje i bilješke po klasi.

2.1. Globalno tipkanje

Sljedeće tri Java klase koristit će se za ilustraciju globalnog uključivanja metapodataka tipa.

Vozilo superrazred:

javni sažetak klase Vozilo {private String make; model privatnog niza; zaštićeno vozilo (String make, String model) {this.make = make; this.model = model; } // no-arg konstruktor, getteri i postavljači}

Automobil podrazred:

javni razred automobila proširuje vozilo {privatni int sjedeći kapacitet; privatni dvostruki vrh brzine; javni automobil (marka niza, model niza, kapacitet za sjedenje, dvostruka brzina) {super (marka, model); this.seatingCapacity = kapacitet sjedećih mjesta; this.topSpeed ​​= topSpeed; } // no-arg konstruktor, getteri i postavljači}

Kamion podrazred:

kamion javne klase produžuje vozilo {private double payloadCapacity; javni kamion (marka niza, model niza, dvostruka nosivost) {super (marka, model); this.payloadCapacity = payloadCapacity; } // no-arg konstruktor, getteri i postavljači}

Globalno zadano tipkanje omogućuje da se podaci o tipu deklariraju samo jednom omogućavanjem na ObjectMapper objekt. Tada će se metapodaci tog tipa primijeniti na sve određene tipove. Kao rezultat toga, vrlo je prikladno koristiti ovu metodu za dodavanje metapodataka tipa, posebno kada je u pitanju velik broj tipova. Loša strana je ta što koristi potpuno kvalificirana imena tipova Java kao identifikatore tipova, te je stoga neprikladna za interakciju sa sustavima koji nisu Java, a primjenjiva je samo na nekoliko unaprijed definiranih vrsta tipova.

The Vozilo gore prikazana struktura koristi se za popunjavanje instance Flota razred:

flota javne klase {vozila s privatne liste; // geteri i postavljači}

Da bismo ugradili metapodatke tipa, moramo omogućiti funkciju tipkanja na ObjectMapper objekt koji će se kasnije koristiti za serializaciju i deserializaciju objekata podataka:

ObjectMapper.enableDefaultTyping (ObjectMapper.DefaultTyping primjenjivost, JsonTypeInfo.As includeAs)

The primjenjivost parametar određuje vrste koje zahtijevaju informacije o tipu, a uključujuAs parametar je mehanizam za uključivanje metapodataka tipa. Uz to, dvije druge varijante enableDefaultTyping metode su osigurane:

  • ObjectMapper.enableDefaultTyping (ObjectMapper.DefaultTyping primjenjivost): omogućuje pozivatelju da navede primjenjivost, dok koristite WRAPPER_ARRAY kao zadana vrijednost za uključujuAs
  • ObjectMapper.enableDefaultTyping (): koristi OBJECT_AND_NON_CONCRETE kao zadana vrijednost za primjenjivost i WRAPPER_ARRAY kao zadana vrijednost za uključujuAs

Pogledajmo kako to funkcionira. Za početak moramo stvoriti ObjectMapper objekt i omogućite zadano tipkanje na njemu:

Mapa ObjectMapper = novi ObjectMapper (); mapper.enableDefaultTyping ();

Sljedeći je korak instanciranje i popunjavanje strukture podataka predstavljene na početku ovog pododjeljka. Kôd za to ponovit će se kasnije u sljedećim pododjeljcima. Radi praktičnosti i ponovne upotrebe nazvat ćemo ga blok instanciranja vozila.

Automobil = novi automobil ("Mercedes-Benz", "S500", 5, 250,0); Kamion kamiona = novi kamion ("Isuzu", "NQR", 7500.0); Popis vozila = novi ArrayList (); vozila.dodati (automobil); vozila.dodati (kamion); Flota serializedFleet = nova flota (); serializedFleet.setVehicles (vozila);

Zatim će se ti naseljeni objekti serializirati:

Niz jsonDataString = mapper.writeValueAsString (serializedFleet);

Dobiveni JSON niz:

{"vozila": ["java.util.ArrayList", [["org.baeldung.jackson.inheritance.Car", {"make": "Mercedes-Benz", "model": "S500", "capacityCapacity" : 5, "topSpeed": 250.0}], ["org.baeldung.jackson.inheritance.Truck", {"make": "Isuzu", "model": "NQR", "payloadCapacity": 7500.0}]]]] }

Tijekom deserializacije, objekti se obnavljaju iz JSON niza uz očuvanje podataka o tipu:

Flota deserializedFleet = mapper.readValue (jsonDataString, Fleet.class);

Obnovljeni objekti bit će iste konkretne podvrste kao i prije serializacije:

assertThat (deserializedFleet.getVehicles (). get (0), instanceOf (Car.class)); assertThat (deserializedFleet.getVehicles (). get (1), instanceOf (Truck.class));

2.2. Bilješke po razredima

Bilješke po razredima moćna su metoda koja uključuje informacije o tipu i može biti vrlo korisna u složenim slučajevima korištenja gdje je potrebna značajna razina prilagodbe. Međutim, to se može postići samo nauštrb komplikacija. Bilješke po klasi nadjačavaju globalno zadano tipkanje ako su informacije o tipu konfigurirane na oba načina.

Da bi se koristila ova metoda, supertip treba označiti s @JsonTypeInfo i nekoliko drugih relevantnih napomena. Ovaj će se pododjeljak koristiti podatkovnim modelom sličnim modelu Vozilo struktura u prethodnom primjeru za ilustraciju napomena po razredima. Jedina promjena je dodavanje bilješki na Vozilo sažetak klase, kao što je prikazano dolje:

@JsonTypeInfo (koristite = JsonTypeInfo.Id.NAME, uključuju = JsonTypeInfo.As.PROPERTY, svojstvo = "type") @JsonSubTypes ({@Type (value = Car.class, name = "car"), @Type (value = Truck.class, name = "truck")}) javna apstraktna klasa Vozilo {// polja, konstruktori, geteri i postavljači}

Objekti podataka kreiraju se pomoću blok instanciranja vozila uveden u prethodnom pododjeljku, a zatim serializiran:

Niz jsonDataString = mapper.writeValueAsString (serializedFleet);

Serijalizacija daje sljedeću JSON strukturu:

{"vozila": [{"type": "car", "make": "Mercedes-Benz", "model": "S500", "Kapacitet sjedala": 5, "topSpeed": 250.0}, {"type" : "truck", "make": "Isuzu", "model": "NQR", "payloadCapacity": 7500.0}]}

Taj se niz koristi za ponovno stvaranje objekata podataka:

Flota deserializedFleet = mapper.readValue (jsonDataString, Fleet.class);

Konačno, potvrđuje se čitav napredak:

assertThat (deserializedFleet.getVehicles (). get (0), instanceOf (Car.class)); assertThat (deserializedFleet.getVehicles (). get (1), instanceOf (Truck.class));

3. Zanemarivanje svojstava iz supertipa

Ponekad neka svojstva naslijeđena od superrazreda trebaju biti zanemarena tijekom serializacije ili deserializacije. To se može postići jednom od tri metode: napomene, miješanje i introspekcija napomena.

3.1. Bilješke

Dvije su često korištene Jacksonove bilješke za zanemarivanje svojstava, a to su @JsonIgnore i @JsonIgnoreProperties. Prvi se izravno primjenjuje na članove tipa, govoreći Jacksonu da zanemari odgovarajuće svojstvo prilikom serializacije ili deserializacije. Potonji se koristi na bilo kojoj razini, uključujući vrstu i člana tipa, da bi se popisala svojstva koja treba zanemariti.

@JsonIgnoreProperties je moćniji od drugog jer nam omogućuje da zanemarimo svojstva naslijeđena od supertipova nad kojima nemamo kontrolu, poput tipova u vanjskoj knjižnici. Uz to, ova nam napomena omogućuje zanemarivanje mnogih svojstava odjednom, što u nekim slučajevima može dovesti do razumljivijeg koda.

Sljedeća struktura klase koristi se za demonstraciju upotrebe napomena:

javni sažetak klase Vozilo {private String make; model privatnog niza; zaštićeno vozilo (String make, String model) {this.make = make; this.model = model; } // no-arg konstruktor, getteri i postavljači} @JsonIgnoreProperties ({"model", "sittingCapacity"}) javna apstraktna klasa Car produžuje vozilo {private int sittingCapacity; @JsonIgnore private double topSpeed; zaštićeni automobil (marka niza, model niza, kapacitet za sjedenje, dvostruka brzina) {super (marka, model); this.seatingCapacity = kapacitet sjedećih mjesta; this.topSpeed ​​= topSpeed; } // no-arg konstruktor, geteri i postavljači} sedan javne klase proširuje Car {javni sedan (marka niza, model niza, int sjedišteKapacitet, dvostruka brzina) {super (marka, model, sjedišteKapacitet, vrhSpeed); } // no-arg constructor} cross class javne klase proširuje Car {private double towingCapacity; javni Crossover (marka niza, model niza, int kapacitet sjedenja, dvostruka brzina, dvostruki kapacitet vuče) {super (marka, model, kapacitet karata, vrh brzine); this.towingCapacity = vučni kapacitet; } // no-arg konstruktor, getteri i postavljači}

Kao što vidiš, @JsonIgnore kaže Jacksonu da ignorira Car.topSpeed vlasništvo, dok @JsonIgnoreProperties ignorira Vozilo.model i Kapacitet sjedala za automobil one.

Ponašanje obje bilješke potvrđuje se sljedećim testom. Prvo, moramo instancirati ObjectMapper i klase podataka, onda to upotrijebite ObjectMapper instanca za serializaciju objekata podataka:

Mapa ObjectMapper = novi ObjectMapper (); Limuzina limuzina = nova limuzina ("Mercedes-Benz", "S500", 5, 250,0); Crossover crossover = novi Crossover ("BMW", "X6", 5, 250.0, 6000.0); Popis vozila = novi ArrayList (); vozila.dodaj (limuzina); vozila.dodaj (crossover); Niz jsonDataString = mapper.writeValueAsString (vozila);

jsonDataString sadrži sljedeći JSON niz:

[{"make": "Mercedes-Benz"}, {"make": "BMW", "kapacitet vuče": 6000,0}]

Na kraju ćemo dokazati prisutnost ili odsutnost različitih imena svojstava u rezultirajućem JSON nizu:

assertThat (jsonDataString, containsString ("make")); assertThat (jsonDataString, ne (sadržiString ("model"))); assertThat (jsonDataString, ne (sadržiString ("kapacitetKapaciteta"))); assertThat (jsonDataString, ne (sadržiString ("topSpeed"))); assertThat (jsonDataString, containsString ("towingCapacity"));

3.2. Mješavine

Kombinacije nam omogućuju primjenu ponašanja (poput ignoriranja svojstava prilikom serializacije i deserializacije) bez potrebe za izravnom primjenom bilješki na klasu. To je posebno korisno kada se radi s trećim klasama u kojima ne možemo izravno modificirati kôd.

Ovaj pododjeljak ponovno koristi lanac nasljeđivanja klasa uveden u prethodnom, osim što @JsonIgnore i @JsonIgnoreProperties bilješke na Automobil razred je uklonjen:

javni sažetak klase Automobil proširuje vozilo {privatni int kapacitetCapacity; privatni dvostruki vrh brzine; // polja, konstruktori, getteri i postavljači}

Kako bismo prikazali rad miješanja, zanemarit ćemo Vozilo.izraditi i Car.topSpeed svojstva, a zatim pomoću testa provjerite radi li sve kako se očekuje.

Prvi korak je deklariranje vrste miješanja:

privatna apstraktna klasa CarMixIn {@JsonIgnore public String make; @JsonIgnore public String topSpeed; }

Dalje, miješanje je vezano za klasu podataka putem ObjectMapper objekt:

Mapa ObjectMapper = novi ObjectMapper (); mapper.addMixIn (Car.class, CarMixIn.class);

Nakon toga instanciramo podatkovne objekte i serializiramo ih u niz:

Limuzina limuzina = nova limuzina ("Mercedes-Benz", "S500", 5, 250,0); Crossover crossover = novi Crossover ("BMW", "X6", 5, 250.0, 6000.0); Popis vozila = novi ArrayList (); vozila.dodaj (limuzina); vozila.dodaj (crossover); Niz jsonDataString = mapper.writeValueAsString (vozila);

jsonDataString sada sadrži sljedeći JSON:

[{"model": "S500", "Kapacitet sjedećih mjesta": 5}, {"model": "X6", "Kapacitet sjedećih mjesta": 5, "Kapacitet vuče": 6000,0}]]

Na kraju, provjerimo rezultat:

assertThat (jsonDataString, ne (containsString ("make"))); assertThat (jsonDataString, containsString ("model")); assertThat (jsonDataString, containsString ("capacityCapacity")); assertThat (jsonDataString, ne (sadržiString ("topSpeed"))); assertThat (jsonDataString, containsString ("towingCapacity"));

3.3. Bilješka Introspekcija

Introspekcija bilješki najmoćnija je metoda za zanemarivanje svojstava supertipa, jer omogućuje detaljnu prilagodbu pomoću AnnotationIntrospector.hasIgnoreMarker API.

Ovaj pododjeljak koristi istu hijerarhiju razreda kao i prethodni. U ovom ćemo slučaju tražiti od Jacksona da to ignorira Vozilo.model, Crossover.towingCapacity i sva svojstva prijavljena u Automobil razred. Počnimo s deklaracijom klase koja proširuje JacksonAnnotationIntrospector sučelje:

klasa IgnoranceIntrospector proširuje JacksonAnnotationIntrospector {public boolean hasIgnoreMarker (AnnotatedMember m)}

Introspector će zanemariti bilo koja svojstva (to jest, tretirat će ih kao da su označena kao zanemarena pomoću neke od drugih metoda) koja odgovaraju skupu uvjeta definiranih u metodi.

Sljedeći je korak registriranje instance IgnoranceIntrospector razred s an ObjectMapper objekt:

Mapa ObjectMapper = novi ObjectMapper (); mapper.setAnnotationIntrospector (novi IgnoranceIntrospector ());

Sada stvaramo i serializiramo podatkovne objekte na isti način kao u odjeljku 3.2. Sadržaj novoproizvedene žice je:

[{"make": "Mercedes-Benz"}, {"make": "BMW"}]

Na kraju ćemo provjeriti je li introspektor radio kako je predviđeno:

assertThat (jsonDataString, containsString ("make")); assertThat (jsonDataString, ne (sadržiString ("model"))); assertThat (jsonDataString, ne (sadržiString ("kapacitetKapaciteta"))); assertThat (jsonDataString, ne (sadržiString ("topSpeed"))); assertThat (jsonDataString, ne (sadržiString ("towingCapacity")));

4. Scenariji rukovanja podtipom

Ovo će se poglavlje baviti dvama zanimljivim scenarijima relevantnim za rukovanje podrazredima.

4.1. Pretvorba između podtipova

Jackson omogućuje pretvaranje objekta u vrstu koja nije originalna. Zapravo, ova se pretvorba može dogoditi među bilo kojim kompatibilnim vrstama, ali je od najveće pomoći kada se koristi između dvije podvrste istog sučelja ili klase za osiguravanje vrijednosti i funkcionalnosti.

Da bismo demonstrirali pretvorbu tipa u drugi, ponovno ćemo upotrijebiti Vozilo hijerarhija preuzeta iz odjeljka 2, uz dodatak @JsonIgnore bilješka o svojstvima u Automobil i Kamion kako bi se izbjegla nespojivost.

javni razred automobila proširuje vozilo {@JsonIgnore private int seatCapacity; @JsonIgnore private double topSpeed; // konstruktori, geteri i postavljači} Kamion javne klase proširuje Vehicle {@JsonIgnore private double payloadCapacity; // konstruktori, getteri i postavljači}

Sljedeći će kôd provjeriti je li pretvorba uspješna i da li novi objekt čuva vrijednosti podataka iz starog:

Mapa ObjectMapper = novi ObjectMapper (); Automobil = novi automobil ("Mercedes-Benz", "S500", 5, 250,0); Kamion kamiona = mapper.convertValue (automobil, Truck.class); assertEquals ("Mercedes-Benz", truck.getMake ()); assertEquals ("S500", truck.getModel ());

4.2. Deserijalizacija bez konstruktora bez argumenata

Prema zadanim postavkama, Jackson ponovno stvara podatkovne objekte pomoću konstruktora no-arg. To je nezgodno u nekim slučajevima, na primjer kada klasa ima ne-zadane konstruktore, a korisnici moraju pisati ne-arg samo da bi zadovoljili Jacksonove zahtjeve. Još je problematičnije u hijerarhiji klase gdje se konstruktor no-arg mora dodati klasi i svim onim višim u lancu nasljeđivanja. U tim slučajevima, metode kreatora priskoči u pomoć.

Ovaj će odjeljak koristiti objektnu strukturu sličnu onoj u odjeljku 2, s nekim promjenama na konstruktorima. Točnije, svi no-arg konstruktori se odbacuju, a konstruktori betonskih podtipova se bilježe @JsonCreator i @JsonProperty kako bi ih učinili metodama kreatora.

javni razred automobila produžuje vozilo {@JsonCreator javni automobil (@JsonProperty ("make") String make, @JsonProperty ("model") String model, @JsonProperty ("sjedeće") int seatCapacity, @JsonProperty ("topSpeed") double topSpeed ) {super (marka, model); this.seatingCapacity = kapacitet sjedećih mjesta; this.topSpeed ​​= topSpeed; } // polja, geteri i postavljači} kamion javne klase proširuje vozilo {@JsonCreator javni kamion (@JsonProperty ("make") String make, @JsonProperty ("model") String model, @JsonProperty ("payload") double payloadCapacity) {super (marka, model); this.payloadCapacity = payloadCapacity; } // polja, geteri i postavljači}

Test će potvrditi da se Jackson može nositi s objektima kojima nedostaju ne-arg konstruktori:

Mapa ObjectMapper = novi ObjectMapper (); mapper.enableDefaultTyping (); Automobil = novi automobil ("Mercedes-Benz", "S500", 5, 250,0); Kamion kamiona = novi kamion ("Isuzu", "NQR", 7500.0); Popis vozila = novi ArrayList (); vozila.dodati (automobil); vozila.dodati (kamion); Flota serializedFleet = nova flota (); serializedFleet.setVehicles (vozila); Niz jsonDataString = mapper.writeValueAsString (serializedFleet); mapper.readValue (jsonDataString, Fleet.class);

5. Zaključak

Ovaj je vodič obuhvatio nekoliko zanimljivih slučajeva korištenja za demonstraciju Jacksonove podrške nasljeđivanju tipova, s naglaskom na polimorfizmu i nepoznavanju svojstava supertipa.

Implementacija svih ovih primjera i isječaka koda može se naći u projektu GitHub.


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