Jackson vs Gson

1. Uvod

U ovom ćemo članku usporediti API-je Gson i Jackson za serializaciju i deserializaciju JSON podataka s Java objektima i obrnuto.

Gson i Jackson su cjelovite knjižnice koje nude JSON podršku za vezivanje podataka za Javu. Svi su aktivno razvijeni projekti otvorenog koda koji nude obradu složenih vrsta podataka i podršku za Java Generics.

I u većini slučajeva, obje knjižnice mogu se deserijalizirati na entitet bez izmjene klase entiteta, što je važno u slučajevima kada programer nema pristup izvornom kodu entiteta.

2. Ovisnost Gsona Mavena

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

Najnoviju verziju Gsona možete dobiti ovdje.

3. Gsonska serializacija

Serijalizacija pretvara Java objekte u JSON izlaz. Razmotrimo sljedeće entitete:

javna klasa ActorGson {private String imdbId; privatno Date dateOfBirth; privatna lista filmografija; // geteri i postavljači, izostavljeni zadani konstruktor i konstruktor polja} film javne klase {private String imdbId; privatni gudački redatelj; privatni glumci s popisa; // izostavljeni getteri i postavljači, izostavljeni zadani konstruktor i konstruktor polja}

3.1. Jednostavna serializacija

Počnimo s primjerom serializacije Jave do JSON-a:

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

To će rezultirati:

{"imdbId": "tt0472043", "director": "Mel Gibson", "glumci": [{"imdbId": "nm2199632", "dateOfBirth": "21. rujna 1982. 00:00:00", " filmografija ": [" Apocalypto "," Beatdown "," Wind Walkers "]}]}

Prema zadanim postavkama:

  • Sva su svojstva serializirana jer nemaju null vrijednosti
  • Datum rođenja polje prevedeno je sa zadanim uzorkom datuma Gson
  • Izlaz nije formatiran, a imena svojstava JSON odgovaraju Java entitetima

3.2. Prilagođena serializacija

Korištenje prilagođenog serializatora omogućuje nam izmjenu standardnog ponašanja. Možemo predstaviti format za izlaz s HTML-om, handle null vrijednosti, izuzeti svojstva iz rezultata ili dodati novi izlaz.

ActorGsonSerializer mijenja generiranje JSON koda za GlumacGson element:

javna klasa ActorGsonSerializer implementira JsonSerializer {private SimpleDateFormat sdf = new SimpleDateFormat ("dd-MM-yyyy"); @Override public JsonElement serialize (ActorGson glumac, tip tipa, JsonSerializationContext jsonSerializationContext) {JsonObjectctorJsonObj = new JsonObject (); actorJsonObj.addProperty ("IMDB kod", glumac.getImdbId ()); glumacJsonObj.addProperty ("Datum rođenja", glumac.getDateOfBirth ()! = null? sdf.format (actor.getDateOfBirth ()): null); glumacJsonObj.addProperty ("N ° film: ", actor.getFilmography ()! = null? glum.getFilmography (). size (): null); glumacJsonObj.addProperty (" filmografija ", actor.getFilmography ()! = null? convertFilmography (glumac.getFilmography ()): null); return glumacJsonObj;} private String convertFilmography (Popis filmografije) {return filmography.stream () .collect (Collectors.joining ("-"));}}

Da bi se isključio direktor vlasništvo, @Izložiti napomena se koristi za svojstva koja želimo uzeti u obzir:

javna klasa MovieWithNullValue {@Expose private String imdbId; privatni gudački redatelj; @ Izložiti glumce s privatnog popisa; }

Sada možemo nastaviti s izradom Gson objekta pomoću GsonBuilder razred:

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

Rezultat je sljedeći:

{"imdbId": null, "glumci": [{"IMDB kod":" nm2199632 ","Datum rođenja": "21-09-1982", "N ° film: ": 3," filmography ":" Apocalypto-Beatdown-Wind Walkers "}]}

Primijeti da:

  • izlaz je formatiran
  • neka svojstva svojstava se mijenjaju i sadrže HTML
  • null vrijednosti su uključene, a direktor polje je izostavljeno
  • Datum je sada u dd-MM-yyyy format
  • prisutno je novo svojstvo - N ° Film
  • filmografija je formatirano svojstvo, a ne zadani JSON popis

4. Gsonova deserijalizacija

4.1. Jednostavna deserijalizacija

Deserijalizacija pretvara JSON ulaz u Java objekte. Da bismo ilustrirali rezultate, implementiramo toString () metoda u obje klase entiteta:

film javne klase {@Override public String toString () {return "Film [imdbId =" + imdbId + ", director =" + director + ", glumci =" + glumci + "]"; } ...} javna klasa ActorGson {@Override public String toString () {return "ActorGson [imdbId =" + imdbId + ", dateOfBirth =" + dateOfBirth + ", filmografija =" + filmografija + "]"; } ...}

Zatim koristimo serializirani JSON i provodimo ga kroz standardnu ​​Gson deserijalizaciju:

Niz jsonInput = "{\" imdbId \ ": \" tt0472043 \ ", \" glumci \ ":" + "[{{" imdbId \ ": \" nm2199632 \ ", \" dateOfBirth \ ": \" 1982- 09-21T12: 00: 00 + 01: 00 \ "," + "\" filmografija \ ": [\" Apocalypto \ ", \" Beatdown \ ", \" Šetači vjetra \ "]}]}"; Izlaz filmaMovie = novi Gson (). FromJson (jsonInput, Movie.class); outputMovie.toString ();

Rezultat smo mi, naši entiteti, popunjeni podacima s našeg JSON ulaza:

Film [imdbId = tt0472043, redatelj = null, glumci = [ActorGson [imdbId = nm2199632, dateOfBirth = utorak 21. rujna 04:00:00 PDT 1982, filmografija = [Apocalypto, Beatdown, Wind Walkers]]]]

Kao što je bio slučaj s jednostavnim serializatorom:

  • imena unosa JSON moraju se podudarati s imenima Java entiteta ili su postavljena na nulu.
  • Datum rođenja polje prevedeno je sa zadanim uzorkom datuma Gson, zanemarujući vremensku zonu.

4.2. Prilagođena deserijalizacija

Korištenje prilagođenog deserijalizatora omogućuje nam izmjenu standardnog ponašanja deserializatora. U ovom slučaju želimo da datum odražava ispravnu vremensku zonu za Datum rođenja. Koristimo običaj ActorGsonDeserializer na GlumacGson entitet da to postigne:

javna klasa ActorGsonDeserializer implementira JsonDeserializer {private SimpleDateFormat sdf = new SimpleDateFormat ("yyyy-MM-dd'T'HH: mm: ss"); @Override public ActorGson deserialize (JsonElement json, Type type, JsonDeserializationContext jsonDeserializationContext) baca JsonParseException {JsonObject jsonObject = json.getAsJsonObject (); JsonElement jsonImdbId = jsonObject.get ("imdbId"); JsonElement jsonDateOfBirth = jsonObject.get ("dateOfBirth"); JsonArray jsonFilmography = jsonObject.getAsJsonArray ("filmografija"); ArrayList filmList = novi ArrayList (); if (jsonFilmography! = null) {for (int i = 0; i <jsonFilmography.size (); i ++) {filmList.add (jsonFilmography.get (i) .getAsString ()); }} ActorGson glumacGson = novi ActorGson (jsonImdbId.getAsString (), sdf.parse (jsonDateOfBirth.getAsString ()), filmList); povratak glumacGson; }}

Zaposlili smo a SimpleDateFormat parser za raščlanjivanje ulaznog datuma, uzimajući u obzir vremensku zonu.

Imajte na umu da smo mogli odlučiti jednostavno napisati prilagođeni deserijalizator samo za Date, ali za ActorGsonDeserializer nudi detaljniji prikaz postupka deserializacije.

Također imajte na umu da Gson pristup ne zahtijeva modificiranje GlumacGson entiteta, što je idealno jer možda nećemo uvijek imati pristup ulaznom entitetu. Ovdje koristimo prilagođeni deserijalizator:

Niz jsonInput = "{\" imdbId \ ": \" tt0472043 \ ", \" glumci \ ":" + "[{{" imdbId \ ": \" nm2199632 \ ", \" dateOfBirth \ ": \" 1982- 09-21T12: 00: 00 + 01: 00 \ ", + \" filmografija \ ": [\" Apocalypto \ ", \" Beatdown \ ", \" Šetači vjetra \ "]}]}"; Gson gson = novi GsonBuilder () .registerTypeAdapter (ActorGson.class, novi ActorGsonDeserializer ()) .create (); Izlaz filmaMovie = gson.fromJson (jsonInput, Movie.class); outputMovie.toString ();

Izlaz je sličan jednostavnom rezultatu deserijalizatora, osim što datum koristi ispravnu vremensku zonu:

Film [imdbId = tt0472043, redatelj = null, glumci = [ActorGson [imdbId = nm2199632, dateOfBirth = utorak, 21. rujna 12:00:00 PDT 1982., filmografija = [Apocalypto, Beatdown, Wind Walkers]]]]

5. Ovisnost Jacksona Mavena

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

Najnoviju verziju Jacksona možete dobiti ovdje.

6. Jackson serializacija

6.1. Jednostavna serializacija

Ovdje ćemo koristiti Jackson za dobivanje istog serijskog sadržaja koji smo imali s Gsonom koristeći sljedeće entitete. Imajte na umu da getteri / postavljači entiteta moraju biti javni:

javna klasa ActorJackson {private String imdbId; privatno Date dateOfBirth; privatna lista filmografija; // potrebni getteri i postavljači, zadani konstruktor // i detalji konstruktora polja izostavljeni} public class Movie {private String imdbId; privatni gudački redatelj; privatni glumci s popisa; // potrebni getteri i postavljači, zadani konstruktor // i detalji konstruktora polja su izostavljeni} SimpleDateFormat sdf = novi SimpleDateFormat ("dd-MM-yyyy"); ActorJackson rudyYoungblood = novi ActorJackson ("nm2199632", sdf.parse ("21-09-1982"), Arrays.asList ("Apocalypto", "Beatdown", "Wind Walkers")); Filmski film = novi film ("tt0472043", "Mel Gibson", Arrays.asList (rudyYoungblood)); Mapa ObjectMapper = novi ObjectMapper (); Niz jsonResult = mapper.writeValueAsString (film);

Izlaz je sljedeći:

{"imdbId": "tt0472043", "redatelj": "Mel Gibson", "glumci": [{"imdbId": "nm2199632", "dateOfBirth": 401439600000, "filmografija": ["Apocalypto", "Beatdown" , "Šetači vjetra"]}]}

Neke zanimljive bilješke:

  • ObjectMapper je naš Jackson-ov serializator / deserializator
  • Izlazni JSON nije formatiran
  • Java Date je prema zadanim postavkama preveden na dugo vrijednost

6.2. Prilagođena serializacija

Možemo stvoriti Jackson serializer za Glumac Jackson generiranje elemenata proširivanjem StdSerializer-a za naš entitet. Opet imajte na umu da dohvatnici / postavljatelji entiteta moraju biti javni:

javna klasa ActorJacksonSerializer proširuje StdSerializer {private SimpleDateFormat sdf = new SimpleDateFormat ("dd-MM-yyyy"); javni ActorJacksonSerializer (klasa t) {super (t); } @Override public void serialize (ActorJackson glumac, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) baca IOException {jsonGenerator.writeStartObject (); jsonGenerator.writeStringField ("imdbId", glumac.getImdbId ()); jsonGenerator.writeObjectField ("dateOfBirth", actor.getDateOfBirth ()! = null? sdf.format (actor.getDateOfBirth ()): null); jsonGenerator.writeNumberField ("Br. filma:", glumac.getFilmography ()! = null? actor.getFilmography (). size (): null); jsonGenerator.writeStringField ("filmografija", glumac.getFilmography () .stream (). collect (Collectors.joining ("-"))); jsonGenerator.writeEndObject (); }}

Stvaramo Movie entitet kako bismo omogućili ignoriranje direktor polje:

javna klasa MovieWithNullValue {private String imdbId; @JsonIgnore private String director; glumci s privatnog popisa; // potrebni getteri i postavljači, zadani konstruktor // i detalji konstruktora polja izostavljeni}

Sada možemo nastaviti s običajem ObjectMapper izrada i postavljanje:

SimpleDateFormat sdf = novi SimpleDateFormat ("dd-MM-yyyy"); ActorJackson rudyYoungblood = novi ActorJackson ("nm2199632", sdf.parse ("21-09-1982"), Arrays.asList ("Apocalypto", "Beatdown", "Wind Walkers")); MovieWithNullValue movieWithNullValue = novo MovieWithNullValue (null, "Mel Gibson", Arrays.asList (rudyYoungblood)); Modul SimpleModule = novi SimpleModule (); module.addSerializer (novi ActorJacksonSerializer (ActorJackson.class)); Mapa ObjectMapper = novi ObjectMapper (); Niz jsonResult = mapper.registerModule (modul) .writer (novi DefaultPrettyPrinter ()) .writeValueAsString (movieWithNullValue);

Izlaz je formatiran JSON koji obrađuje null vrijednosti, formatira datum, isključuje direktor polje i prikazuje novi izlaz N °:

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

7. Jacksonova deserijalizacija

7.1. Jednostavna deserijalizacija

Da bismo ilustrirali rezultate, implementiramo toString () metoda u obje Jacksonove klase entiteta:

film javne klase {@Override public String toString () {return "Film [imdbId =" + imdbId + ", director =" + director + ", glumci =" + glumci + "]"; } ...} javna klasa ActorJackson {@Override public String toString () {return "ActorJackson [imdbId =" + imdbId + ", dateOfBirth =" + dateOfBirth + ", filmography =" + filmography + "]"; } ...}

Zatim koristimo serializirani JSON i provodimo ga kroz Jacksonovu deserializaciju:

Niz jsonInput = "{\" imdbId \ ": \" tt0472043 \ ", \" glumci \ ": [{\" imdbId \ ": \" nm2199632 \ ", \" dateOfBirth \ ": \" 1982-09-21T12 : 00: 00 + 01: 00 \ ", \" filmografija \ ": [\" Apocalypto \ ", \" Beatdown \ ", \" Šetači vjetra \ "]}]}"; Mapa ObjectMapper = novi ObjectMapper (); Filmski film = mapper.readValue (jsonInput, Movie.class);

Rezultat smo mi, naši entiteti, popunjeni podacima s našeg JSON ulaza:

Film [imdbId = tt0472043, redatelj = null, glumci = [ActorJackson [imdbId = nm2199632, dateOfBirth = utorak 21. rujna 04:00:00 PDT 1982, filmografija = [Apocalypto, Beatdown, Wind Walkers]]]]

Kao što je bio slučaj s jednostavnim serializatorom:

  • imena unosa JSON moraju odgovarati imenima Java entiteta ili su postavljena na null,
  • Datum rođenja polje prevedeno je sa zadanim Jackson uzorkom datuma, zanemarujući vremensku zonu.

7.2. Prilagođena deserijalizacija

Korištenje prilagođenog deserijalizatora omogućuje nam izmjenu standardnog ponašanja deserializatora.

U ovom slučaju želimo da datum odražava ispravnu vremensku zonu za Datum rođenja, pa dodajemo DateFormatter našem Jacksonu ObjectMapper:

Niz jsonInput = "{\" imdbId \ ": \" tt0472043 \ ", \" direktor \ ": \" Mel Gibson \ ", \" glumci \ ": [{\" imdbId \ ": \" nm2199632 \ ", \ "dateOfBirth \": \ "1982-09-21T12: 00: 00 + 01: 00 \", \ "filmografija \": [\ "Apocalypto \", \ "Beatdown \", \ "Šetači vjetra \"] }]} "; Mapa ObjectMapper = novi ObjectMapper (); DateFormat df = novi SimpleDateFormat ("yyyy-MM-dd'T'HH: mm: ss"); mapper.setDateFormat (df); Filmski film = mapper.readValue (jsonInput, Movie.class); movie.toString ();

Izlaz odražava ispravnu vremensku zonu s datumom:

Film [imdbId = tt0472043, redatelj = Mel Gibson, glumci = [ActorJackson [imdbId = nm2199632, dateOfBirth = utorak 21. rujna 12:00:00 PDT 1982, filmografija = [Apokalipto, Beatdown, Šetači vjetra]]]]

Ovo rješenje je čisto i jednostavno.

Alternativno, mogli smo stvoriti prilagođeni deserijalizator za Glumac Jackson razreda, registrirao je ovaj modul na našem ObjectMapper, i deserijalizirao datum pomoću @JsonDeserialize bilješka na Glumac Jackson entitet.

Nedostatak tog pristupa je potreba za izmjenom entiteta, što možda nije idealno za slučajeve kada nemamo pristup ulaznim klasama entiteta.

8. Zaključak

I Gson i Jackson dobre su opcije za serializaciju / deserializaciju JSON podataka, jednostavne za upotrebu i dobro dokumentirane.

Prednosti Gsona:

  • Jednostavnost toJson/odJson u jednostavnim slučajevima
  • Za deserializaciju ne treba pristup Java entitetima

Prednosti Jacksona:

  • Ugrađen u sve okvire JAX-RS (Jersey, Apache CXF, RESTEasy, Restlet) i Spring
  • Opsežna podrška za bilješke

Kôd za Gsona i Jacksona možete pronaći na GitHubu.