Jackson vs Gson

1. Perkenalan

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

Gson dan Jackson adalah pustaka lengkap yang menawarkan dukungan pengikatan data JSON untuk Java. Masing-masing secara aktif mengembangkan proyek sumber terbuka yang menawarkan untuk menangani tipe data kompleks dan dukungan untuk Java Generics.

Dan dalam banyak kasus, kedua pustaka dapat melakukan deserialisasi ke entitas tanpa mengubah kelas entitas, yang penting dalam kasus di mana pengembang tidak memiliki akses ke kode sumber entitas.

2. Ketergantungan Gson Maven

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

Anda bisa mendapatkan Gson versi terbaru di sini.

3. Serialisasi Gson

Serialisasi mengubah objek Java menjadi keluaran JSON. Pertimbangkan entitas 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 Sederhana

Mari kita mulai 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 default:

  • Semua properti dibuat berseri karena tidak memiliki nilai null
  • Bidang dateOfBirth diterjemahkan dengan pola tanggal Gson default
  • Output tidak diformat dan nama properti JSON sesuai dengan entitas Java

3.2. Serialisasi Kustom

Menggunakan penyambung ubahsuaian memungkinkan kami mengubah perilaku standar. Kita dapat memperkenalkan pemformat keluaran dengan HTML, menangani nilai null , mengecualikan properti dari keluaran, atau menambahkan keluaran baru.

ActorGsonSerializer mengubah pembuatan kode 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 properti director , anotasi @Expose digunakan untuk properti yang ingin kami pertimbangkan:

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

Sekarang kita dapat melanjutkan 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 sebagai berikut:

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

Perhatikan itu:

  • hasilnya diformat
  • beberapa nama properti diubah dan berisi HTML
  • nilai null disertakan, dan bidang direktur dihilangkan
  • Tanggal sekarang di dd-MM-yyyy Format
  • properti baru hadir - N ° Film
  • filmografi adalah properti yang diformat, bukan daftar JSON default

4. Gson Deserialization

4.1. Deserialisasi Sederhana

Deserialization mengubah masukan JSON menjadi objek Java. Untuk menggambarkan output, kami mengimplementasikan metode toString () di kedua kelas entitas:

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 serial dan menjalankannya melalui deserialisasi Gson standar:

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();

Outputnya adalah kami, entitas 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 halnya dengan serializer sederhana:

  • nama input JSON harus sesuai dengan nama entitas Java, atau disetel ke null.
  • Bidang dateOfBirth diterjemahkan dengan pola tanggal Gson default, mengabaikan zona waktu.

4.2. Deserialisasi Kustom

Menggunakan deserializer khusus memungkinkan kita mengubah perilaku deserializer standar. Dalam kasus ini, kami ingin tanggal mencerminkan zona waktu yang benar untuk dateOfBirth . Kami menggunakan ActorGsonDeserializer kustom di entitas 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 mengurai tanggal input, memperhitungkan zona waktu.

Perhatikan bahwa kami dapat memutuskan untuk hanya menulis deserializer kustom hanya untuk Tanggal, tetapi ActorGsonDeserializer menawarkan tampilan yang lebih rinci dari proses deserialization.

Perhatikan juga bahwa pendekatan Gson tidak memerlukan modifikasi entitas ActorGson , yang ideal karena kami mungkin tidak selalu memiliki akses ke entitas 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();

Outputnya mirip dengan hasil deserializer sederhana, kecuali tanggalnya menggunakan zona waktu yang benar:

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 bisa mendapatkan Jackson versi terbaru di sini.

6. Serialisasi Jackson

6.1. Serialisasi Sederhana

Di sini kita akan menggunakan Jackson untuk mendapatkan konten serial yang sama yang kita miliki dengan Gson menggunakan entitas berikut. Perhatikan bahwa pengambil / penyetel entitas harus publik:

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);

Outputnya adalah sebagai berikut:

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

Beberapa catatan menarik:

  • ObjectMapper adalah serializer / deserializer Jackson kami
  • JSON keluaran tidak diformat
  • Secara default, Tanggal Java diterjemahkan ke dalam 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:

  • Dibangun ke dalam semua framework JAX-RS (Jersey, Apache CXF, RESTEasy, Restlet), dan Spring
  • Dukungan anotasi ekstensif

Anda dapat menemukan kode untuk Gson dan Jackson di GitHub.