Öröklés Jackson-szal

1. Áttekintés

Ebben a cikkben áttekintjük a Jackson osztályhierarchiákkal való munkát.

Két tipikus felhasználási eset az altípus metaadatainak felvétele és a szuperosztályoktól örökölt tulajdonságok figyelmen kívül hagyása. Leírjuk ezt a két forgatókönyvet és néhány olyan körülményt, amikor az altípusok speciális kezelésére van szükség.

2. Az altípusra vonatkozó információk felvétele

Az adatobjektumok sorosítása és dezerializálása során kétféle módon adható meg a típusinformáció: globális alapértelmezett tipizálás és osztályonkénti kommentárok.

2.1. Globális alapértelmezett gépelés

A következő három Java osztály segítségével szemléltetjük a típusú metaadatok globális felvételét.

Jármű szuperosztály:

public absztrakt osztály Jármű {private String make; privát húr modell; védett jármű (húr gyártmány, húr modell) {this.make = gyártmány; ez.modell = modell; } // no-arg konstruktor, getterek és setterek}

Autó alosztály:

public class Autó meghosszabbítja Jármű {private int sittingCapacity; privát dupla topSpeed; public Car (Vonós márka, Vonós modell, int üléskapacitás, dupla topSpeed) {szuper (márka, modell); this.seatingCapacity = ülésCapacity; this.topSpeed ​​= topSpeed; } // no-arg konstruktor, getterek és setterek}

Kamion alosztály:

nyilvános osztályú teherautó meghosszabbítja Jármű {private double payloadCapacity; nyilvános teherautó (húr gyártmány, húr modell, dupla hasznos teher) kapacitás) {szuper (márka, modell); this.payloadCapacity = payloadCapacity; } // no-arg konstruktor, getterek és setterek}

A globális alapértelmezett tipizálás lehetővé teszi a típusinformációk egyszeri deklarálását azáltal, hogy engedélyezi azokat egy ObjectMapper tárgy. Ezt a típusú metaadatokat az összes kijelölt típusra alkalmazzák. Ennek eredményeként nagyon kényelmes ezt a módszert használni a típusú metaadatok hozzáadásához, különösen akkor, ha nagy számú típus vesz részt. Hátránya, hogy teljesen minősített Java típusú neveket használ típusazonosítóként, ezért alkalmatlan a nem Java rendszerekkel való interakciókra, és csak több, előre meghatározott típusú típusra alkalmazható.

A Jármű A fent bemutatott struktúrát a Flotta osztály:

közkategória Flotta {magánlista járművek; // szerelők és beállítók}

A típusú metaadatok beágyazásához engedélyeznünk kell a gépelési funkciót a ObjectMapper objektum, amelyet később az adatobjektumok sorosítására és deszerializálására használnak:

ObjectMapper.enableDefaultTyping (ObjectMapper.DefaultTyping alkalmazhatóság, JsonTypeInfo.As includeAs)

A alkalmazhatóság paraméter határozza meg a típusinformációt igénylő típusokat, és a tartalmazzaAs A paraméter a típusú metaadatok felvételének mechanizmusa. Ezenkívül a enableDefaultTyping módszerek:

  • ObjectMapper.enableDefaultTyping (ObjectMapper.DefaultTyping alkalmazhatóság): lehetővé teszi a hívó számára a alkalmazhatósághasználat közben WRAPPER_ARRAY alapértelmezett értékeként tartalmazzaAs
  • ObjectMapper.enableDefaultTyping (): használ OBJECT_AND_NON_CONCRETE alapértelmezett értékeként alkalmazhatóság és WRAPPER_ARRAY alapértelmezett értékeként tartalmazzaAs

Nézzük meg, hogyan működik. Először létre kell hoznunk egy ObjectMapper objektumot, és engedélyezze az alapértelmezett gépelést:

ObjectMapper mapper = új ObjectMapper (); mapper.enableDefaultTyping ();

A következő lépés az alszakasz elején bevezetett adatszerkezet példányosítása és feltöltése. Az ehhez szükséges kódot később felhasználjuk a következő alfejezetekben. A kényelem és az újrafelhasználás érdekében megnevezzük jármű példányosítási blokk.

Autóautó = új autó ("Mercedes-Benz", "S500", 5, 250,0); Teherautó teherautó = új teherautó ("Isuzu", "NQR", 7500.0); Járművek = new ArrayList (); járművek.add (autó); járművek.add (teherautó); Flotta serializedFleet = új Flotta (); serializedFleet.setVehicles (járművek);

Ezeket a lakott objektumokat sorosítják:

Karakterlánc jsonDataString = mapper.writeValueAsString (serializedFleet);

Az így kapott JSON karakterlánc:

{"járművek": ["java.util.ArrayList", [["org.baeldung.jackson.inheritance.Car", {"gyártmány": "Mercedes-Benz", "modell": "S500", "üléskapacitás" : 5, "topSpeed": 250.0}], ["org.baeldung.jackson.inheritance.Truck", {"make": "Isuzu", "model": "NQR", "payloadCapacity": 7500.0}]]] }

A deszerializálás során az objektumok a JSON karakterláncból kerülnek helyreállításra, a típusadatok megőrzésével:

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

Az újjáépített objektumok ugyanazok a konkrét altípusok lesznek, mint a sorozatosítás előtt:

assertThat (deserializedFleet.getVehicles (). get (0), instanceOf (Autóosztály)); assertThat (deserializedFleet.getVehicles (). get (1), instanceOf (Truck.class));

2.2. Osztályonkénti kommentárok

Az osztályonkénti kommentárok hatékony módszerek a típusinformációk beillesztésére, és nagyon hasznosak lehetnek olyan összetett használati esetekben, amikor jelentős szintű testreszabásra van szükség. Ez azonban csak a bonyodalmak rovására érhető el. Az osztályonkénti kommentárok felülírják a globális alapértelmezett gépelést, ha a típusinformációk mindkét módon konfigurálva vannak.

Ennek a módszernek a használatához a szupertípust fel kell tüntetni @JsonTypeInfo és számos más releváns kommentár. Ez az alszakasz a Jármű struktúra az előző példában osztályonkénti kommentárok szemléltetésére. Az egyetlen változás a következő jelölések hozzáadása Jármű absztrakt osztály, az alábbiak szerint:

@JsonTypeInfo (use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") @JsonSubTypes ({@Type (value = Car.class, name = "car"), @Type (value = Truck.class, name = "truck")}) public abstract class Jármű {// mezők, kivitelezők, szerelők és beállítók}

Az adatobjektumokat a jármű példányosítási blokk az előző alfejezetben bevezetett, majd sorosított:

Karakterlánc jsonDataString = mapper.writeValueAsString (serializedFleet);

A sorosítás a következő JSON struktúrát eredményezi:

{"járművek": [{"type": "car", "make": "Mercedes-Benz", "model": "S500", "sittingCapacity": 5, "topSpeed": 250.0}, {"type" : "truck", "make": "Isuzu", "model": "NQR", "payloadCapacity": 7500.0}]}}

Ezt a karakterláncot használják adatobjektumok újrateremtésére:

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

Végül a teljes előrelépés érvényesül:

assertThat (deserializedFleet.getVehicles (). get (0), instanceOf (Autóosztály)); assertThat (deserializedFleet.getVehicles (). get (1), instanceOf (Truck.class));

3. A tulajdonságok figyelmen kívül hagyása egy Supertype-ből

Néha a szuperosztályokból örökölt tulajdonságokat figyelmen kívül kell hagyni a sorozatosítás vagy a deserializálás során. Ez a három módszer egyikével érhető el: annotációk, mix-inek és annotációk introspekciója.

3.1. Megjegyzések

A tulajdonságok figyelmen kívül hagyására két általánosan használt Jackson-annotáció van @JsonIgnore és @JsonIgnoreProperties. Az előbbit közvetlenül a típusú tagokra alkalmazzák, mondván Jacksonnak, hogy a sorosítás vagy a deszerializálás során hagyja figyelmen kívül a megfelelő tulajdonságot. Az utóbbit bármilyen szinten, beleértve a típust és a tagtípust is, használják a figyelmen kívül hagyandó tulajdonságok felsorolására.

@JsonIgnoreProperties erősebb, mint a másik, mivel lehetővé teszi számunkra, hogy figyelmen kívül hagyjuk azokat a szupertípusoktól örökölt tulajdonságokat, amelyek felett nincs kontrollunk, például a külső könyvtárban található típusokat. Ráadásul ez a megjegyzés lehetővé teszi számunkra, hogy egyszerre sok tulajdonságot figyelmen kívül hagyjunk, ami egyes esetekben érthetőbb kódhoz vezethet.

A következő osztályszerkezetet használjuk az annotáció használatának bemutatására:

public absztrakt osztály Jármű {private String make; privát húr modell; védett jármű (húr gyártmány, húr modell) {this.make = gyártmány; ez.modell = modell; } // no-arg konstruktor, getterek és beállítók} @JsonIgnoreProperties ({"model", "sittingCapacity"}) public absztrakt osztály Car extends Vehicle {private int sittingCapacity; @JsonIgnore private double topSpeed; védett autó (húr gyártmány, húr modell, int üléskapacitás, dupla topSpeed) {szuper (márka, modell); this.seatingCapacity = ülésCapacity; this.topSpeed ​​= topSpeed; } // no-arg konstruktor, getterek és beállítók} public class Sedan extends Car {public Sedan (String make, String model, int sittingCapacity, double topSpeed) {super (make, model, sittingCapacity, topSpeed); } // no-arg konstruktor} public class Crossover meghosszabbítja a Car {private double towingCapacity; public crossover (String make, String model, int sittingCapacity, double topSpeed, double towingCapacity) {szuper (gyártmány, modell, üléskapacitás, topSpeed); this.towingCapacity = towingCapacity; } // no-arg konstruktor, getterek és setterek}

Amint látod, @JsonIgnore azt mondja Jacksonnak, hogy hagyja figyelmen kívül Car.topSpeed tulajdon, míg @JsonIgnoreProperties figyelmen kívül hagyja a Jármű.modell és Autó.ülésKapacitás azok.

Mindkét annotáció viselkedését a következő teszt igazolja. Először is példát kell tennünk ObjectMapper és adatosztályok, majd használja azt ObjectMapper példány az adatobjektumok sorosításához:

ObjectMapper mapper = új ObjectMapper (); Sedan sedan = új Sedan ("Mercedes-Benz", "S500", 5, 250,0); Crossover crossover = új crossover ("BMW", "X6", 5, 250,0, 6000,0); Járművek = new ArrayList (); járművek.add (szedán); járművek.add (crossover); Karakterlánc jsonDataString = mapper.writeValueAsString (járművek);

jsonDataString a következő JSON tömböt tartalmazza:

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

Végül be fogjuk bizonyítani a különféle tulajdonságnevek jelenlétét vagy hiányát a kapott JSON karakterláncban:

assertThat (jsonDataString, tartalmazString ("make")); assertThat (jsonDataString, nem (tartalmazzaString ("modell"))); assertThat (jsonDataString, nem (tartalmazzaString ("üléskapacitás"))); assertThat (jsonDataString, nem (tartalmazzaString ("topSpeed"))); assertThat (jsonDataString, tartalmazString ("towingCapacity"));

3.2. Keveredések

A keverékek lehetővé teszik a viselkedés alkalmazását (például a tulajdonságok figyelmen kívül hagyását a sorosítás és a deserializálás során) anélkül, hogy feliratkozásokat kellene közvetlenül alkalmazni egy osztályra. Ez különösen akkor hasznos, ha harmadik féltől származó osztályokkal foglalkozunk, amelyekben nem tudjuk közvetlenül módosítani a kódot.

Ez az alszakasz újból felhasználja az előzőben bevezetett osztályörökláncot, azzal a különbséggel, hogy a @JsonIgnore és @JsonIgnoreProperties jegyzetek a Autó osztály eltávolítva:

public absztrakt osztály Autó meghosszabbítja Jármű {private int sittingCapacity; privát dupla topSpeed; // mezők, kivitelezők, mérőeszközök és beállítók}

A keverések működésének bemutatása érdekében figyelmen kívül hagyjuk Jármű.make és Car.topSpeed tulajdonságait, majd teszt segítségével ellenőrizze, hogy minden a várt módon működik-e.

Az első lépés egy mix-in típus deklarálása:

saját absztrakt osztály CarMixIn {@JsonIgnore public String make; @JsonIgnore public String topSpeed; }

Ezután a keverés egy adatosztályhoz van kötve egy ObjectMapper tárgy:

ObjectMapper mapper = új ObjectMapper (); mapper.addMixIn (Car.class, CarMixIn.class);

Ezt követően példányosítjuk az adatobjektumokat, és sorba állítjuk őket karakterláncokká:

Sedan sedan = új Sedan ("Mercedes-Benz", "S500", 5, 250,0); Crossover crossover = új crossover ("BMW", "X6", 5, 250,0, 6000,0); Járművek listája = new ArrayList (); járművek.add (szedán); járművek.add (crossover); Karakterlánc jsonDataString = mapper.writeValueAsString (járművek);

jsonDataString most a következő JSON-t tartalmazza:

[{"model": "S500", "sittingCapacity": 5}, {"model": "X6", "üléskapacitás": 5, "towingCapacity": 6000.0}]

Végül ellenőrizzük az eredményt:

assertThat (jsonDataString, nem (tartalmazzaString ("make"))); assertThat (jsonDataString, tartalmazzaString ("modell")); assertThat (jsonDataString, tartalmazString ("üléskapacitás")); assertThat (jsonDataString, nem (tartalmazzaString ("topSpeed"))); assertThat (jsonDataString, tartalmazString ("towingCapacity"));

3.3. Annotation Introspection

Az annotációs önvizsgálat a leghatékonyabb módszer a szupertípus tulajdonságainak figyelmen kívül hagyására, mivel lehetővé teszi a részletes testreszabást a AnnotationIntrospector.hasIgnoreMarker API.

Ez az alszakasz ugyanazt az osztályhierarchiát használja, mint az előző. Ebben a felhasználási esetben arra kérjük Jacksont, hogy hagyja figyelmen kívül Jármű.modell, Crossover.towingCapacity és az összes, a Autó osztály. Kezdjük egy olyan osztály deklarációjával, amely kiterjeszti a JacksonAnnotationIntrospector felület:

class IgnoranceIntrospector kiterjeszti a JacksonAnnotationIntrospector {public boolean hasIgnoreMarker (AnnotatedMember m)}

Az introspektor figyelmen kívül hagyja azokat a tulajdonságokat (vagyis úgy kezeli őket, mintha azokat a többi módszer valamelyikénél figyelmen kívül hagyták volna), amelyek megfelelnek a módszerben meghatározott feltételek halmazának.

A következő lépés a IgnoranceIntrospector osztály egy ObjectMapper tárgy:

ObjectMapper mapper = új ObjectMapper (); mapper.setAnnotationIntrospector (új IgnoranceIntrospector ());

Most ugyanúgy létrehozunk és sorosítunk adatobjektumokat, mint a 3.2 szakaszban. Az újonnan gyártott karakterlánc tartalma:

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

Végül ellenőrizni fogjuk, hogy az introspektor rendeltetésszerűen működött-e:

assertThat (jsonDataString, tartalmazString ("make")); assertThat (jsonDataString, nem (tartalmazzaString ("modell"))); assertThat (jsonDataString, nem (tartalmazzaString ("üléskapacitás"))); assertThat (jsonDataString, nem (tartalmazzaString ("topSpeed"))); assertThat (jsonDataString, not (tartalmazzaString ("towingCapacity")));

4. altípus-kezelési forgatókönyvek

Ez a szakasz két érdekes szcenárióval foglalkozik, amelyek relevánsak az alosztálykezelés szempontjából.

4.1. Átalakítás altípusok között

Jackson lehetővé teszi egy objektum átalakítását az eredetitől eltérő típusra. Valójában ez az átalakítás bármilyen kompatibilis típus között megtörténhet, de a leghasznosabb, ha ugyanazon interfész vagy osztály két altípusa között használják az értékek és a funkcionalitás biztosítására.

Annak bemutatására, hogy egy típus átalakul-e másikká, újra felhasználjuk a Jármű a 2. szakaszból vett hierarchia, a @JsonIgnore megjegyzés a Autó és Kamion az összeférhetetlenség elkerülése érdekében.

nyilvános osztályú autó meghosszabbítja a járművet {@JsonIgnore private int sittingCapacity; @JsonIgnore private double topSpeed; // kivitelezők, szerelvények és beállítók} nyilvános osztályú teherautó meghosszabbítja a járművet {@JsonIgnore private double payloadCapacity; // kivitelezők, szerelvények és beállítók}

A következő kód ellenőrzi, hogy az átalakítás sikeres-e, és hogy az új objektum megőrzi-e a régi értékeit:

ObjectMapper mapper = új ObjectMapper (); Autóautó = új autó ("Mercedes-Benz", "S500", 5, 250,0); Teherautó kamion = mapper.convertValue (személygépkocsi, Truck.class); assertEquals ("Mercedes-Benz", truck.getMake ()); assertEquals ("S500", truck.getModel ());

4.2. Dezerializálás argon nélküli konstruktorok nélkül

Alapértelmezés szerint Jackson no-arg konstruktorok segítségével újrateremti az adatobjektumokat. Ez bizonyos esetekben kellemetlen, például amikor egy osztálynak nem alapértelmezett konstruktorai vannak, és a felhasználóknak argonokat kell írniuk, csak Jackson követelményeinek kielégítése érdekében. Még problémásabb egy osztályhierarchiában, amikor egy argum nélküli konstruktort kell hozzáadni egy osztályhoz, és mindazokat, akik magasabbak az öröklődési láncban. Ezekben az esetekben, alkotói módszerek jöjjön a mentő.

Ez a szakasz a 2. szakaszban találhatóhoz hasonló objektumstruktúrát fog használni, a konstruktorokon néhány változtatással. Pontosabban az összes argum nélküli konstruktort eldobják, és a konkrét altípusok konstruktorait feljegyzik @JsonCreator és @JsonProperty hogy alkotói módszerekké váljanak.

a nyilvános osztályú autó meghosszabbítja a járművet. ) {szuper (márka, modell); this.seatingCapacity = ülésCapacity; this.topSpeed ​​= topSpeed; } // mezők, getterek és beállítók} public class Teherautó meghosszabbítja a járművet. {szuper (márka, modell); this.payloadCapacity = payloadCapacity; } // mezők, mérőeszközök és beállítók}

Egy teszt meggyőződik arról, hogy Jackson képes-e olyan objektumokkal foglalkozni, amelyekben nincs arg-konstruktor:

ObjectMapper mapper = új ObjectMapper (); mapper.enableDefaultTyping (); Autó = új autó ("Mercedes-Benz", "S500", 5, 250,0); Teherautó teherautó = új teherautó ("Isuzu", "NQR", 7500,0); Járművek = new ArrayList (); járművek.add (autó); járművek.add (teherautó); Flotta serializedFleet = új Flotta (); serializedFleet.setVehicles (járművek); Karakterlánc jsonDataString = mapper.writeValueAsString (serializedFleet); mapper.readValue (jsonDataString, Fleet.class);

5. Következtetés

Ez az oktatóanyag számos érdekes felhasználási esetet ismertetett, hogy bemutassa Jackson támogatását a típusöröklésben, a polimorfizmusra és a szupertípus tulajdonságainak ismeretlenségére összpontosítva.

Ezen példák és kódrészletek megvalósítása megtalálható egy GitHub projektben.