Ringer til standard Serializer fra Custom Serializer i Jackson

1. Introduksjon

Serialisering av den komplette datastrukturen vår til JSON ved hjelp av en nøyaktig en-til-en-representasjon av alle feltene er kanskje ikke hensiktsmessig noen ganger, eller bare ikke det vi ønsker. I stedet, Det kan være lurt å lage et utvidet eller forenklet syn på dataene våre. Dette er hvor tilpassede Jackson-serialisatorer kommer til spill.

Imidlertid kan det være kjedelig å implementere en tilpasset serializer, spesielt hvis modellobjektene våre har mange felt, samlinger eller nestede objekter. Heldigvis har Jackson-biblioteket flere bestemmelser som kan gjøre denne jobben mye enklere.

I denne korte opplæringen tar vi en titt på tilpassede Jackson-serien og viser hvordan du får tilgang til standard serialiserere i en tilpasset serializer.

2. Eksempel på datamodell

Før vi dykker inn i tilpasningen av Jackson, la oss ta en titt på utvalget vårt Mappe klasse som vi ønsker å serieisere:

offentlig klasse Mappe {privat Lang id; privat strengnavn; privat streng eier; privat Dato opprettet; privat Dato endret; privat Dato lastAccess; private Listefiler = ny ArrayList (); // standard getters og setters} 

Og Fil klasse, som er definert som en Liste inne i vår Mappe klasse:

offentlig klasse Fil {privat Lang id; privat strengnavn; // standard getters og setters} 

3. Custom Serializers i Jackson

Den største fordelen med å bruke tilpassede serialisatorer er at vi ikke trenger å endre klassestrukturen. I tillegg til, vi kan enkelt koble fra den forventede oppførselen vår fra klassen selv.

La oss forestille oss at vi ønsker et redusert syn på vårt Mappe klasse:

{"name": "Root Folder", "files": [{"id": 1, "name": "File 1"}, {"id": 2, "name": "File 2"}]} 

Som vi ser i de neste avsnittene, er det flere måter vi kan oppnå ønsket produksjon i Jackson på.

3.1. Brute Force-tilnærming

Først, uten å bruke Jacksons standardserialiserer, kan vi lage en egendefinert serialiseringsenhet der vi løfter alt tungt.

La oss lage en tilpasset serializer for vår Mappe klasse for å oppnå dette:

public class FolderJsonSerializer utvider StdSerializer {public FolderJsonSerializer () {super (Folder.class); } @Override public void serialize (Mappeverdi, JsonGenerator gen, SerializerProvider-leverandør) kaster IOException {gen.writeStartObject (); gen.writeStringField ("name", value.getName ()); gen.writeArrayFieldStart ("filer"); for (Filfil: value.getFiles ()) {gen.writeStartObject (); gen.writeNumberField ("id", file.getId ()); gen.writeStringField ("navn", file.getName ()); gen.writeEndObject (); } gen.writeEndArray (); gen.writeEndObject (); }}

Dermed kan vi serialisere våre Mappe klasse til en redusert visning som bare inneholder feltene vi ønsker.

3.2. Bruke Internt ObjectMapper

Selv om tilpassede serielliserere gir oss fleksibiliteten til å endre hver eiendom i detalj, kan vi gjøre jobben vår enklere gjenbruk av Jacksons standardserier.

En måte å bruke standard serialiserere på er å få tilgang til det interne ObjectMapper klasse:

@ Override public void serialize (Mappeverdi, JsonGenerator gen, SerializerProvider-leverandør) kaster IOException {gen.writeStartObject (); gen.writeStringField ("name", value.getName ()); ObjectMapper mapper = (ObjectMapper) gen.getCodec (); gen.writeFieldName ("filer"); String stringValue = mapper.writeValueAsString (value.getFiles ()); gen.writeRawValue (stringValue); gen.writeEndObject (); } 

Så, Jackson håndterer ganske enkelt tunge løft ved å serieisere Liste av Fil objekter, og da vil produksjonen vår være den samme.

3.3. Ved hjelp av SerializerProvider

En annen måte å ringe til standardseriene er å bruke SerializerProvider. Derfor delegerer vi prosessen til standardserien av typen Fil.

La oss nå forenkle koden vår litt ved hjelp av SerializerProvider:

@Override public void serialize (Mappeverdi, JsonGenerator gen, SerializerProvider-leverandør) kaster IOException {gen.writeStartObject (); gen.writeStringField ("name", value.getName ()); provider.defaultSerializeField ("filer", value.getFiles (), gen); gen.writeEndObject (); } 

Og, akkurat som før, får vi den samme produksjonen.

4. Et mulig rekursjonsproblem

Avhengig av brukssaken, kan det hende vi trenger å utvide våre serielle data ved å inkludere mer informasjon for Mappe. Dette kan være for et eldre system eller en ekstern applikasjon som skal integreres som vi ikke har en sjanse til å endre.

La oss endre serialisereren vår for å lage en detaljer feltet for seriell data for å bare utsette alle feltene i Mappe klasse:

@Override public void serialize (Mappeverdi, JsonGenerator gen, SerializerProvider-leverandør) kaster IOException {gen.writeStartObject (); gen.writeStringField ("name", value.getName ()); provider.defaultSerializeField ("filer", value.getFiles (), gen); // denne linjen forårsaker unntaksleverandør.defaultSerializeField ("detaljer", verdi, gen); gen.writeEndObject (); } 

Denne gangen får vi en StackOverflowError unntak.

Når vi definerer en egendefinert serialisering, overstyrer Jackson originalen internt BeanSerializer forekomst som er opprettet for typen Mappe. Derfor vår SerializerProvider finner den tilpassede serialisereren hver gang, i stedet for den som er standard, og dette forårsaker en uendelig løkke.

Så hvordan løser vi dette problemet? Vi ser en brukbar løsning for dette scenariet i neste avsnitt.

5. Bruke BeanSerializerModifier

En mulig løsning bruker BeanSerializerModifierfor å lagre standard serialisereren for typen Mappefør Jackson internt overstyrer det.

La oss modifisere serialisereren vår og legge til et ekstra felt - defaultSerializer:

privat slutt JsonSerializer defaultSerializer; offentlig FolderJsonSerializer (JsonSerializer defaultSerializer) {super (Folder.class); this.defaultSerializer = defaultSerializer; } 

Deretter lager vi en implementering av BeanSerializerModifier for å passere standardserien:

offentlig klasse FolderBeanSerializerModifier utvider BeanSerializerModifier {@Override public JsonSerializer modifySerializer (SerializationConfig config, BeanDescription beanDesc, JsonSerializer serializer) {if (beanDesc.getBeanClass (). equals (Foldererial)) } returner serializer; }} 

Nå må vi registrere våre BeanSerializerModifier som en modul for å få det til å fungere:

ObjectMapper mapper = ny ObjectMapper (); SimpleModule-modul = ny SimpleModule (); module.setSerializerModifier (ny FolderBeanSerializerModifier ()); mapper.registerModule (modul); 

Deretter bruker vi defaultSerializer for detaljer felt:

@ Override public void serialize (Mappeverdi, JsonGenerator gen, SerializerProvider-leverandør) kaster IOException {gen.writeStartObject (); gen.writeStringField ("name", value.getName ()); provider.defaultSerializeField ("filer", value.getFiles (), gen); gen.writeFieldName ("detaljer"); defaultSerializer.serialize (verdi, gen, leverandør); gen.writeEndObject (); } 

Til slutt vil vi kanskje fjerne filer felt fra detaljer siden vi allerede skriver det inn i de serielle dataene separat.

Så vi bare ignorerer filer felt i vårt Mappe klasse:

@JsonIgnorer private listefiler = ny ArrayList (); 

Til slutt er problemet løst, og vi får også forventet produksjon:

{"name": "Root Folder", "files": [{"id": 1, "name": "File 1"}, {"id": 2, "name": "File 2"}], "details": {"id": 1, "name": "Root Folder", "owner": "root", "created": 1565203657164, "modified": 1565203657164, "lastAccess": 1565203657164}} 

6. Konklusjon

I denne veiledningen, vi lærte hvordan vi kan kalle standard serialiserere i en tilpasset serializer i Jackson Library.

Som alltid er alle kodeeksemplene som brukes i denne opplæringen, tilgjengelige på GitHub.


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