Jackson vs Gson

1. Introduktion

I denne artikel sammenligner vi Gson og Jackson API'er til serialisering og deserialisering af JSON-data til Java-objekter og omvendt.

Gson og Jackson er komplette biblioteker, der tilbyder JSON databindende support til Java. Hver er aktivt udviklede open source-projekter, der tilbyder håndtering af komplekse datatyper og support til Java Generics.

Og i de fleste tilfælde kan begge biblioteker deserialisere til en enhed uden at ændre en enhedsklasse, hvilket er vigtigt i tilfælde, hvor en udvikler ikke har adgang til enhedens kildekode.

2. Gson Maven afhængighed

 com.google.code.gson gson $ {gson.version} 

Du kan få den nyeste version af Gson her.

3. Gson-serialisering

Serialisering konverterer Java-objekter til JSON-output. Overvej følgende enheder:

offentlig klasse ActorGson {privat streng imdbId; privat Dato datoFødsel; privat Filmografi; // getters and setters, default constructor and field constructor uteladt} public class Movie {private String imdbId; privat streng direktør; private listeaktører; // getters og setters, standard konstruktør og felt konstruktør udeladt}

3.1. Enkel serialisering

Lad os starte med et eksempel på Java til JSON-serialisering:

SimpleDateFormat sdf = ny SimpleDateFormat ("dd-MM-åååå"); ActorGson rudyYoungblood = ny ActorGson ("nm2199632", sdf.parse ("21-09-1982"), Arrays.asList ("Apocalypto", "Beatdown", "Wind Walkers")); Filmfilm = ny film ("tt0472043", "Mel Gibson", Arrays.asList (rudyYoungblood)); String serializedMovie = ny Gson (). ToJson (film);

Dette vil resultere i:

{"imdbId": "tt0472043", "instruktør": "Mel Gibson", "skuespillere": [{"imdbId": "nm2199632", "dateOfBirth": "21. september 1982 12:00:00", " filmografi ": [" Apocalypto "," Beatdown "," Wind Walkers "]}}}

Som standard:

  • Alle egenskaber er seriel, fordi de ikke har noget nul værdier
  • fødselsdato felt blev oversat med standard Gson-datomønsteret
  • Output er ikke formateret, og JSON-egenskabsnavne svarer til Java-enhederne

3.2. Tilpasset serialisering

Brug af en tilpasset serializer giver os mulighed for at ændre standardadfærden. Vi kan introducere et outputformater med HTML, håndtag nul værdier, ekskluder egenskaber fra output, eller tilføj en ny output.

ActorGsonSerializer ændrer generering af JSON-kode til SkuespillerGson element:

offentlig klasse ActorGsonSerializer implementerer JsonSerializer {private SimpleDateFormat sdf = nye SimpleDateFormat ("dd-MM-åååå"); @Override public JsonElement serialize (ActorGson actor, Type type, JsonSerializationContext jsonSerializationContext) {JsonObject actorJsonObj = new JsonObject (); skuespillerJsonObj.addProperty ("IMDB-kode", actor.getImdbId ()); actorJsonObj.addProperty ("Fødselsdato", actor.getDateOfBirth ()! = null? sdf.format (actor.getDateOfBirth ()): null); actorJsonObj.addProperty ("Nr. Film: ", actor.getFilmography ()! = null? actor.getFilmography (). størrelse (): null); actorJsonObj.addProperty (" filmography ", actor.getFilmography ()! = null? convertFilmography (actor.getFilmography ()): null); return skuespillerJsonObj;} privat String convertFilmography (List filmography) {return filmography.stream () .collect (Collectors.joining ("-"));}}

For at udelukke direktør ejendom, den @Udsætte annotation bruges til egenskaber, vi vil overveje:

offentlig klasse MovieWithNullValue {@Expose private String imdbId; privat streng direktør; @ Udsæt private aktører på listen; }

Nu kan vi fortsætte med Gson-objekt oprettelse ved hjælp af GsonBuilder klasse:

Gson gson = ny GsonBuilder () .setPrettyPrinting () .excludeFieldsWithoutExposeAnnotation () .serializeNulls () .disableHtmlEscaping () .registerTypeAdapter (ActorGson.class, ny ActorGsonSerializer ()) .create () SimpleDateFormat sdf = ny SimpleDateFormat ("dd-MM-åååå"); ActorGson rudyYoungblood = ny ActorGson ("nm2199632", sdf.parse ("21-09-1982"), Arrays.asList ("Apocalypto", "Beatdown", "Wind Walkers")); MovieWithNullValue movieWithNullValue = ny MovieWithNullValue (null, "Mel Gibson", Arrays.asList (rudyYoungblood)); String serializedMovie = gson.toJson (movieWithNullValue);

Resultatet er følgende:

{"imdbId": null, "actor": [{"IMDB-kode":" nm2199632 ","Fødselsdato": "21-09-1982", "Nr. Film: ": 3," filmography ":" Apocalypto-Beatdown-Wind Walkers "}]}

Læg mærke til det:

  • output er formateret
  • nogle ejendomsnavne ændres og indeholder HTML
  • nul værdier er inkluderet, og direktør felt er udeladt
  • Dato er nu i dd-MM-åååå format
  • en ny ejendom er til stede - Nr. Film
  • filmografi er en formateret egenskab, ikke standard JSON-listen

4. Gson-deserialisering

4.1. Enkel deserialisering

Deserialisering konverterer JSON-input til Java-objekter. For at illustrere output implementerer vi toString () metode i begge enhedsklasser:

offentlig klassefilm {@Override public String toString () {return "Film [imdbId =" + imdbId + ", instruktør =" + instruktør + ", skuespillere =" + aktører + "]"; } ...} offentlig klasse ActorGson {@Override public String toString () {return "ActorGson [imdbId =" + imdbId + ", dateOfBirth =" + dateOfBirth + ", filmografi =" + filmografi + "]"; } ...}

Derefter bruger vi den serielle JSON og kører den gennem standard Gson-deserialisering:

String jsonInput = "{\" imdbId \ ": \" tt0472043 \ ", \" actor \ ":" + "[{\" imdbId \ ": \" nm2199632 \ ", \" dateOfBirth \ ": \" 1982- 09-21T12: 00: 00 + 01: 00 \ "," + "\" filmografi \ ": [\" Apocalypto \ ", \" Beatdown \ ", \" Wind Walkers \ "]}]}"; Film outputMovie = ny Gson (). FraJson (jsonInput, Movie.class); outputMovie.toString ();

Outputtet er os vores enheder, befolket med dataene fra vores JSON-input:

Film [imdbId = tt0472043, instruktør = null, skuespillere = [ActorGson [imdbId = nm2199632, dateOfBirth = Tirsdag 21. september 04:00:00 PDT 1982, filmografi = [Apocalypto, Beatdown, Wind Walkers]]]]

Som det var tilfældet med den simple serializer:

  • JSON-inputnavne skal svare til Java-enhedsnavne, ellers er de indstillet til null.
  • fødselsdato felt blev oversat med standard Gson-datomønster, idet tidszonen ignoreres.

4.2. Brugerdefineret deserialisering

Brug af en brugerdefineret deserializer giver os mulighed for at ændre standard deserializer-opførsel. I dette tilfælde ønsker vi, at datoen afspejler den korrekte tidszone for fødselsdato. Vi bruger en brugerdefineret ActorGsonDeserializer på den SkuespillerGson enhed for at opnå dette:

offentlig klasse ActorGsonDeserializer implementerer JsonDeserializer {privat SimpleDateFormat sdf = ny SimpleDateFormat ("åååå-MM-dd'T'HH: mm: ss"); @ Override public ActorGson deserialize (JsonElement json, Type type, JsonDeserializationContext jsonDeserializationContext) kaster JsonParseException {JsonObject jsonObject = json.getAsJsonObject (); JsonElement jsonImdbId = jsonObject.get ("imdbId"); JsonElement jsonDateOfBirth = jsonObject.get ("dateOfBirth"); JsonArray jsonFilmography = jsonObject.getAsJsonArray ("filmografi"); ArrayList filmList = ny ArrayList (); hvis (jsonFilmography! = null) {for (int i = 0; i <jsonFilmography.size (); i ++) {filmList.add (jsonFilmography.get (i) .getAsString ()); }} ActorGson actorGson = ny ActorGson (jsonImdbId.getAsString (), sdf.parse (jsonDateOfBirth.getAsString ()), filmList); retur skuespillerGson; }}

Vi ansatte en SimpleDateFormat parser for at analysere inputdatoen og tage højde for tidszonen.

Bemærk, at vi kunne have besluttet blot at skrive en brugerdefineret deserializer til kun datoen, men den ActorGsonDeserializer giver en mere detaljeret oversigt over deserialiseringsprocessen.

Bemærk også, at Gson-fremgangsmåden ikke kræver ændring af SkuespillerGson enhed, hvilket er ideelt, da vi måske ikke altid har adgang til inputenheden. Vi bruger den brugerdefinerede deserializer her:

String jsonInput = "{\" imdbId \ ": \" tt0472043 \ ", \" actor \ ":" + "[{\" imdbId \ ": \" nm2199632 \ ", \" dateOfBirth \ ": \" 1982- 09-21T12: 00: 00 + 01: 00 \ ", + \" filmografi \ ": [\" Apocalypto \ ", \" Beatdown \ ", \" Wind Walkers \ "]}}}"; Gson gson = ny GsonBuilder () .registerTypeAdapter (ActorGson.class, ny ActorGsonDeserializer ()) .create (); Film outputMovie = gson.fromJson (jsonInput, Movie.class); outputMovie.toString ();

Outputtet svarer til det simple deserializer-resultat, bortset fra datoen bruger den korrekte tidszone:

Film [imdbId = tt0472043, instruktør = null, skuespillere = [ActorGson [imdbId = nm2199632, dateOfBirth = Tirsdag 21. september 12:00:00 PDT 1982, filmografi = [Apocalypto, Beatdown, Wind Walkers]]]]

5. Afhængighed af Jackson Maven

 com.fasterxml.jackson.core jackson-databind $ {jackson.version} 

Du kan få den nyeste version af Jackson her.

6. Jackson Serialization

6.1. Enkel serialisering

Her bruger vi Jackson til at opnå det samme serieindhold, som vi havde med Gson ved hjælp af følgende enheder. Bemærk, at enhedens getters / setters skal være offentlige:

offentlig klasse ActorJackson {private String imdbId; privat Dato datoFødsel; privat Filmografi; // krævede getters og settere, standardkonstruktør // og feltkonstruktørdetaljer udeladt} public class Film {private String imdbId; privat streng direktør; private listeaktører; // krævede getters og settere, standard konstruktør // og felt konstruktør detaljer udeladt} SimpleDateFormat sdf = ny SimpleDateFormat ("dd-MM-åååå"); ActorJackson rudyYoungblood = ny ActorJackson ("nm2199632", sdf.parse ("21-09-1982"), Arrays.asList ("Apocalypto", "Beatdown", "Wind Walkers")); Filmfilm = ny film ("tt0472043", "Mel Gibson", Arrays.asList (rudyYoungblood)); ObjectMapper-kortlægger = ny ObjectMapper (); String jsonResult = mapper.writeValueAsString (film);

Outputtet er som følger:

{"imdbId": "tt0472043", "director": "Mel Gibson", "actor": [{"imdbId": "nm2199632", "dateOfBirth": 401439600000, "filmography": ["Apocalypto", "Beatdown" , "Vindvandrere"]}]}

Nogle interessante noter:

  • ObjectMapper er vores Jackson serializer / deserializer
  • Outputtet JSON er ikke formateret
  • Som standard oversættes Java Date til lang værdi

6.2. Tilpasset serialisering

Vi kan oprette en Jackson-serie til Skuespiller Jackson elementgenerering ved at udvide StdSerializer til vores enhed. Igen bemærk, at enhedens getters / setters skal være offentlige:

offentlig klasse ActorJacksonSerializer udvider StdSerializer {private SimpleDateFormat sdf = ny SimpleDateFormat ("dd-MM-åååå"); offentlig ActorJacksonSerializer (klasse t) {super (t); } @ Override public void serialize (ActorJackson actor, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) kaster IOException {jsonGenerator.writeStartObject (); jsonGenerator.writeStringField ("imdbId", actor.getImdbId ()); jsonGenerator.writeObjectField ("dateOfBirth", actor.getDateOfBirth ()! = null? sdf.format (actor.getDateOfBirth ()): null); jsonGenerator.writeNumberField ("N ° Film:", actor.getFilmography ()! = null? actor.getFilmography (). størrelse (): null); jsonGenerator.writeStringField ("filmography", actor.getFilmography () .stream (). collect (Collectors.joining ("-"))); jsonGenerator.writeEndObject (); }}

Vi opretter en filmenhed, der tillader ignorering af direktør Mark:

offentlig klasse MovieWithNullValue {privat streng imdbId; @JsonIgnorer privat strengdirektør; private listeaktører; // krævede getters og settere, standard konstruktør // og felt konstruktør detaljer udeladt}

Nu kan vi fortsætte med en brugerdefineret ObjectMapper oprettelse og opsætning:

SimpleDateFormat sdf = ny SimpleDateFormat ("dd-MM-åååå"); ActorJackson rudyYoungblood = ny ActorJackson ("nm2199632", sdf.parse ("21-09-1982"), Arrays.asList ("Apocalypto", "Beatdown", "Wind Walkers")); MovieWithNullValue movieWithNullValue = ny MovieWithNullValue (null, "Mel Gibson", Arrays.asList (rudyYoungblood)); SimpleModule-modul = nyt SimpleModule (); module.addSerializer (ny ActorJacksonSerializer (ActorJackson.class)); ObjectMapper-kortlægger = ny ObjectMapper (); String jsonResult = mapper.registerModule (module) .writer (new DefaultPrettyPrinter ()) .writeValueAsString (movieWithNullValue);

Outputtet er formateret JSON, der håndterer nul værdier, formaterer datoen, ekskluderer direktør felt og viser ny output af Nr:

{"actor": [{"imdbId": "nm2199632", "dateOfBirth": "21-09-1982", "N ° Film:": 3, "filmography": "Apocalypto-Beatdown-Wind Walkers"}] , "imdbID": null}

7. Jackson deserialisering

7.1. Enkel deserialisering

For at illustrere output implementerer vi toString () metode i begge Jackson-enhedsklasser:

offentlig klassefilm {@Override public String toString () {return "Film [imdbId =" + imdbId + ", instruktør =" + instruktør + ", skuespillere =" + aktører + "]"; } ...} offentlig klasse ActorJackson {@Override public String toString () {return "ActorJackson [imdbId =" + imdbId + ", dateOfBirth =" + dateOfBirth + ", filmografi =" + filmografi + "]"; } ...}

Derefter bruger vi den serialiserede JSON og kører den gennem Jackson deserialisering:

String jsonInput = "{\" imdbId \ ": \" tt0472043 \ ", \" actor \ ": [{\" imdbId \ ": \" nm2199632 \ ", \" dateOfBirth \ ": \" 1982-09-21T12 : 00: 00 + 01: 00 \ ", \" filmografi \ ": [\" Apocalypto \ ", \" Beatdown \ ", \" Wind Walkers \ "]}]}"; ObjectMapper-kortlægger = ny ObjectMapper (); Filmfilm = mapper.readValue (jsonInput, Movie.class);

Outputtet er os vores enheder, befolket med dataene fra vores JSON-input:

Film [imdbId = tt0472043, instruktør = null, skuespillere = [ActorJackson [imdbId = nm2199632, dateOfBirth = Tirsdag 21. september 04:00:00 PDT 1982, filmografi = [Apocalypto, Beatdown, Wind Walkers]]]]

Som det var tilfældet med den simple serializer:

  • JSON-inputnavne skal svare til Java-enhedsnavne, eller de er indstillet til nul,
  • fødselsdato felt blev oversat med Jackson-datomønsterets standardmønster, idet tidszonen ignoreres.

7.2. Brugerdefineret deserialisering

Brug af en brugerdefineret deserializer giver os mulighed for at ændre standard deserializer-opførsel.

I dette tilfælde ønsker vi, at datoen afspejler den korrekte tidszone for fødselsdato, så vi tilføjer en DateFormatter til vores Jackson ObjectMapper:

String jsonInput = "{\" imdbId \ ": \" tt0472043 \ ", \" instruktør \ ": \" Mel Gibson \ ", \" aktører \ ": [{\" imdbId \ ": \" nm2199632 \ ", \ "dateOfBirth \": \ "1982-09-21T12: 00: 00 + 01: 00 \", \ "filmografi \": [\ "Apocalypto \", \ "Beatdown \", \ "Wind Walkers \"] }]} "; ObjectMapper-kortlægger = ny ObjectMapper (); DateFormat df = ny SimpleDateFormat ("åååå-MM-dd'T'HH: mm: ss"); mapper.setDateFormat (df); Filmfilm = mapper.readValue (jsonInput, Movie.class); movie.toString ();

Outputtet afspejler den korrekte tidszone med datoen:

Film [imdbId = tt0472043, instruktør = Mel Gibson, skuespillere = [ActorJackson [imdbId = nm2199632, dateOfBirth = Tirsdag 21. september 12:00:00 PDT 1982, filmografi = [Apocalypto, Beatdown, Wind Walkers]]]]

Denne løsning er ren og enkel.

Alternativt kunne vi have oprettet en brugerdefineret deserializer til Skuespiller Jackson klasse registrerede dette modul med vores ObjectMapperog deserialiserede datoen ved hjælp af @JsonDeserialize kommentar på Skuespiller Jackson enhed.

Ulempen ved denne tilgang er behovet for at ændre enheden, hvilket muligvis ikke er ideel til tilfælde, hvor vi ikke har adgang til inputenhedsklasser.

8. Konklusion

Både Gson og Jackson er gode muligheder for serialisering / deserialisering af JSON-data, enkle at bruge og veldokumenterede.

Fordele ved Gson:

  • Enkelhed af tilJson/fraJson i de enkle tilfælde
  • For deserialisering behøver du ikke adgang til Java-enheder

Fordele ved Jackson:

  • Indbygget i alle JAX-RS (Jersey, Apache CXF, RESTEasy, Restlet) og Spring framework
  • Omfattende annoteringsstøtte

Du kan finde koden til Gson og Jackson på GitHub.