Jackson vs Gson

1. Pengenalan

Dalam artikel ini, kita akan membandingkan API Gson dan Jackson untuk membuat siri dan deserialisasi data JSON ke objek Java dan sebaliknya.

Gson dan Jackson adalah perpustakaan lengkap yang menawarkan sokongan pengikat data JSON untuk Java. Masing-masing dikembangkan secara aktif projek sumber terbuka yang menawarkan untuk menangani jenis data yang kompleks dan sokongan untuk Java Generics.

Dan dalam kebanyakan kes, kedua perpustakaan dapat mendesialisasikan ke entiti tanpa mengubah kelas entiti, yang penting dalam kes di mana pembangun tidak mempunyai akses ke kod sumber entiti.

2. Ketergantungan Gson Maven

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

Anda boleh mendapatkan versi terbaru Gson di sini.

3. Serialisasi Gson

Serialisasi menukar objek Java ke output JSON. Pertimbangkan entiti berikut:

public class ActorGson { private String imdbId; private Date dateOfBirth; private List filmography; // getters and setters, default constructor and field constructor omitted } public class Movie { private String imdbId; private String director; private List actors; // getters and setters, default constructor and field constructor omitted }

3.1. Serialisasi Ringkas

Mari kita mulakan dengan contoh serialisasi Java ke JSON:

SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy"); ActorGson rudyYoungblood = new ActorGson( "nm2199632", sdf.parse("21-09-1982"), Arrays.asList("Apocalypto", "Beatdown", "Wind Walkers") ); Movie movie = new Movie( "tt0472043", "Mel Gibson", Arrays.asList(rudyYoungblood)); String serializedMovie = new Gson().toJson(movie);

Ini akan menghasilkan:

{ "imdbId": "tt0472043", "director": "Mel Gibson", "actors": [{ "imdbId": "nm2199632", "dateOfBirth": "Sep 21, 1982 12:00:00 AM", "filmography": ["Apocalypto", "Beatdown", "Wind Walkers"] }] }

Secara lalai:

  • Semua sifat bersiri kerana tidak mempunyai nilai kosong
  • medan dateOfBirth diterjemahkan dengan corak tarikh Gson lalai
  • Keluaran tidak diformat dan nama harta JSON sesuai dengan entiti Java

3.2. Serialisasi Tersuai

Dengan menggunakan penyesuai khas membolehkan kita mengubah tingkah laku standard. Kami dapat memperkenalkan pemformat output dengan HTML, menangani nilai nol , mengecualikan sifat dari output, atau menambahkan output baru.

ActorGsonSerializer mengubah generasi kod JSON untuk elemen ActorGson :

public class ActorGsonSerializer implements JsonSerializer { private SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy"); @Override public JsonElement serialize(ActorGson actor, Type type, JsonSerializationContext jsonSerializationContext) { JsonObject actorJsonObj = new JsonObject(); actorJsonObj.addProperty("IMDB Code", actor.getImdbId()); actorJsonObj.addProperty("Date Of Birth", actor.getDateOfBirth() != null ? sdf.format(actor.getDateOfBirth()) : null); actorJsonObj.addProperty("N° Film: ", actor.getFilmography() != null ? actor.getFilmography().size() : null); actorJsonObj.addProperty("filmography", actor.getFilmography() != null ? convertFilmography(actor.getFilmography()) : null); return actorJsonObj; } private String convertFilmography(List filmography) { return filmography.stream() .collect(Collectors.joining("-")); } }

Untuk mengecualikan harta pengarah , anotasi @Expose digunakan untuk hartanah yang ingin kami pertimbangkan:

public class MovieWithNullValue { @Expose private String imdbId; private String director; @Expose private List actors; }

Sekarang kita boleh meneruskan pembuatan objek Gson menggunakan kelas GsonBuilder :

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

Hasilnya adalah seperti berikut:

{ "imdbId": null, "actors": [ { "IMDB Code": "nm2199632", "Date Of Birth": "21-09-1982", "N° Film: ": 3, "filmography": "Apocalypto-Beatdown-Wind Walkers" } ] }

Perhatikan bahawa:

  • output diformat
  • beberapa nama harta diubah dan mengandungi HTML
  • nilai nol disertakan, dan bidang pengarah dihilangkan
  • Tarikh kini dalam format dd-MM-yyyy
  • harta baru ada - Filem N °
  • filemografi adalah harta yang diformat, bukan senarai JSON lalai

4. Dessonisasi Gson

4.1. Deserialisasi ringkas

Deserialization menukar input JSON menjadi objek Java. Untuk menggambarkan output, kami menerapkan kaedah toString () di kedua kelas entiti:

public class Movie { @Override public String toString() { return "Movie [imdbId=" + imdbId + ", director=" + director + ",actors=" + actors + "]"; } ... } public class ActorGson { @Override public String toString() { return "ActorGson [imdbId=" + imdbId + ", dateOfBirth=" + dateOfBirth + ",filmography=" + filmography + "]"; } ... }

Kemudian kami menggunakan JSON bersiri dan menjalankannya melalui deserialisasi Gson standard:

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

Keluarannya adalah entiti kami, diisi dengan data dari input JSON kami:

Movie [imdbId=tt0472043, director=null, actors=[ActorGson [imdbId=nm2199632, dateOfBirth=Tue Sep 21 04:00:00 PDT 1982, filmography=[Apocalypto, Beatdown, Wind Walkers]]]]

Seperti yang berlaku dengan serializer sederhana:

  • nama input JSON mesti sesuai dengan nama entiti Java, atau diset ke null.
  • medan dateOfBirth diterjemahkan dengan corak tarikh Gson lalai, mengabaikan zon waktu.

4.2. Deserialisasi Tersuai

Menggunakan deserializer khusus membolehkan kita mengubah tingkah laku deserializer standard. Dalam kes ini, kami mahu tarikh menggambarkan zon waktu yang betul untuk dateOfBirth . Kami menggunakan ActorGsonDeserializer khas pada entiti ActorGson untuk mencapai ini:

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

Kami menggunakan parser SimpleDateFormat untuk menguraikan tarikh masukan, memperhitungkan zon waktu.

Perhatikan bahawa kita boleh memutuskan untuk hanya menulis deserializer khusus untuk Tarikh sahaja, tetapi ActorGsonDeserializer menawarkan pandangan yang lebih terperinci mengenai proses deserialisasi.

Perhatikan juga bahawa pendekatan Gson tidak memerlukan pengubahan entiti ActorGson , yang sangat sesuai kerana kita mungkin tidak selalu mempunyai akses ke entiti input. Kami menggunakan deserializer khusus di sini:

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

Keluarannya serupa dengan hasil deserializer sederhana, kecuali tarikh menggunakan zon waktu yang betul:

Movie [imdbId=tt0472043, director=null, actors=[ActorGson [imdbId=nm2199632, dateOfBirth=Tue Sep 21 12:00:00 PDT 1982, filmography=[Apocalypto, Beatdown, Wind Walkers]]]]

5. Ketergantungan Jackson Maven

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

Anda boleh mendapatkan versi terbaru Jackson di sini.

6. Serialisasi Jackson

6.1. Serialisasi Ringkas

Di sini kita akan menggunakan Jackson untuk mendapatkan kandungan bersiri yang sama dengan Gson menggunakan entiti berikut. Perhatikan bahawa penerima / penyusun entiti mestilah umum:

public class ActorJackson { private String imdbId; private Date dateOfBirth; private List filmography; // required getters and setters, default constructor // and field constructor details omitted } public class Movie { private String imdbId; private String director; private List actors; // required getters and setters, default constructor // and field constructor details omitted } SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy"); ActorJackson rudyYoungblood = new ActorJackson("nm2199632",sdf.parse("21-09-1982"), Arrays.asList("Apocalypto","Beatdown","Wind Walkers") ); Movie movie = new Movie("tt0472043","Mel Gibson", Arrays.asList(rudyYoungblood)); ObjectMapper mapper = new ObjectMapper(); String jsonResult = mapper.writeValueAsString(movie);

Keluarannya adalah seperti berikut:

{"imdbId":"tt0472043","director":"Mel Gibson","actors": [{"imdbId":"nm2199632","dateOfBirth":401439600000, "filmography":["Apocalypto","Beatdown","Wind Walkers"]}]}

Beberapa nota menarik:

  • ObjectMapper adalah serializer / deserializer Jackson kami
  • Output JSON tidak diformat
  • Secara lalai, Java Date diterjemahkan ke nilai panjang

6.2. Custom Serialization

We can create a Jackson serializer for ActorJackson element generation by extending StdSerializer for our entity. Again note that the entity getters/setters must be public:

public class ActorJacksonSerializer extends StdSerializer { private SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy"); public ActorJacksonSerializer(Class t) { super(t); } @Override public void serialize(ActorJackson actor, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws 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().size() : null); jsonGenerator.writeStringField("filmography", actor.getFilmography() .stream().collect(Collectors.joining("-"))); jsonGenerator.writeEndObject(); } }

We create a Movie entity to allow ignoring of the director field:

public class MovieWithNullValue { private String imdbId; @JsonIgnore private String director; private List actors; // required getters and setters, default constructor // and field constructor details omitted }

Now we can proceed with a custom ObjectMapper creation and setup:

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

The output is formatted JSON that handles null values, formats the date, excludes the director field and shows new output of :

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

7. Jackson Deserialization

7.1. Simple Deserialization

To illustrate the output, we implement the toString() method in both Jackson entity classes:

public class Movie { @Override public String toString() { return "Movie [imdbId=" + imdbId + ", director=" + director + ", actors=" + actors + "]"; } ... } public class ActorJackson { @Override public String toString() { return "ActorJackson [imdbId=" + imdbId + ", dateOfBirth=" + dateOfBirth + ", filmography=" + filmography + "]"; } ... }

Then we utilize the serialized JSON and run it through Jackson deserialization:

String jsonInput = "{\"imdbId\":\"tt0472043\",\"actors\": [{\"imdbId\":\"nm2199632\",\"dateOfBirth\":\"1982-09-21T12:00:00+01:00\", \"filmography\":[\"Apocalypto\",\"Beatdown\",\"Wind Walkers\"]}]}"; ObjectMapper mapper = new ObjectMapper(); Movie movie = mapper.readValue(jsonInput, Movie.class);

The output is us our entities, populated with the data from our JSON input:

Movie [imdbId=tt0472043, director=null, actors=[ActorJackson [imdbId=nm2199632, dateOfBirth=Tue Sep 21 04:00:00 PDT 1982, filmography=[Apocalypto, Beatdown, Wind Walkers]]]]

As was the case with the simple serializer:

  • the JSON input names must correspond with the Java entity names, or they are set to null,
  • dateOfBirth field was translated with the default Jackson date pattern, ignoring the time zone.

7.2. Custom Deserialization

Using a custom deserializer allows us to modify the standard deserializer behavior.

In this case, we want the date to reflect the correct time zone for dateOfBirth, so we add a DateFormatter to our Jackson ObjectMapper:

String jsonInput = "{\"imdbId\":\"tt0472043\",\"director\":\"Mel Gibson\", \"actors\":[{\"imdbId\":\"nm2199632\",\"dateOfBirth\":\"1982-09-21T12:00:00+01:00\", \"filmography\":[\"Apocalypto\",\"Beatdown\",\"Wind Walkers\"]}]}"; ObjectMapper mapper = new ObjectMapper(); DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); mapper.setDateFormat(df); Movie movie = mapper.readValue(jsonInput, Movie.class); movie.toString();

The output reflects the correct time zone with the date:

Movie [imdbId=tt0472043, director=Mel Gibson, actors=[ActorJackson [imdbId=nm2199632, dateOfBirth=Tue Sep 21 12:00:00 PDT 1982, filmography=[Apocalypto, Beatdown, Wind Walkers]]]]

This solution is clean and simple.

Alternatively, we could have created a custom deserializer for the ActorJackson class, registered this module with our ObjectMapper, and deserialized the date using the @JsonDeserialize annotation on the ActorJackson entity.

The disadvantage of that approach is the need to modify the entity, which may not be ideal for cases when we don't have access to the input entity classes.

8. Conclusion

Both Gson and Jackson are good options for serializing/deserializing JSON data, simple to use and well documented.

Advantages of Gson:

  • Simplicity of toJson/fromJson in the simple cases
  • For deserialization, do not need access to the Java entities

Advantages of Jackson:

  • Dibina dalam semua rangka kerja JAX-RS (Jersey, Apache CXF, RESTEasy, Restlet), dan Spring
  • Sokongan anotasi yang luas

Anda boleh mendapatkan kod untuk Gson dan Jackson di GitHub.