Jackson gegen Gson

1. Einleitung

In diesem Artikel werden die Gson- und Jackson-APIs zum Serialisieren und Deserialisieren von JSON-Daten mit Java-Objekten verglichen und umgekehrt.

Gson und Jackson sind vollständige Bibliotheken, die JSON-Datenbindungsunterstützung für Java bieten. Bei jedem handelt es sich um aktiv entwickelte Open-Source-Projekte, die den Umgang mit komplexen Datentypen und die Unterstützung von Java Generics bieten.

In den meisten Fällen können beide Bibliotheken zu einer Entität deserialisieren, ohne eine Entitätsklasse zu ändern. Dies ist wichtig, wenn ein Entwickler keinen Zugriff auf den Entitätsquellcode hat.

2. Gson Maven-Abhängigkeit

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

Die neueste Version von Gson erhalten Sie hier.

3. Gson-Serialisierung

Durch die Serialisierung werden Java-Objekte in JSON-Ausgaben konvertiert. Betrachten Sie die folgenden Entitäten:

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. Einfache Serialisierung

Beginnen wir mit einem Beispiel für die Serialisierung von Java zu 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);

Dies führt zu:

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

Standardmäßig:

  • Alle Eigenschaften werden serialisiert, da sie keine Nullwerte haben
  • Das Feld dateOfBirth wurde mit dem Standard-Gson- Datumsmuster übersetzt
  • Die Ausgabe ist nicht formatiert und die JSON-Eigenschaftsnamen entsprechen den Java-Entitäten

3.2. Benutzerdefinierte Serialisierung

Durch die Verwendung eines benutzerdefinierten Serializers können wir das Standardverhalten ändern. Wir können einen Ausgabeformatierer mit HTML einführen, Nullwerte verarbeiten , Eigenschaften von der Ausgabe ausschließen oder eine neue Ausgabe hinzufügen.

ActorGsonSerializer ändert die Generierung von JSON-Code für das ActorGson- Element:

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

Um die Director- Eigenschaft auszuschließen , wird die Annotation @Expose für Eigenschaften verwendet, die wir berücksichtigen möchten:

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

Jetzt können wir mit der Gson-Objekterstellung mit der GsonBuilder- Klasse fortfahren :

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

Das Ergebnis ist folgendes:

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

Beachte das:

  • Die Ausgabe ist formatiert
  • Einige Eigenschaftsnamen werden geändert und enthalten HTML
  • Nullwerte sind enthalten, und das Director- Feld wird weggelassen
  • Das Datum liegt jetzt im Format TT-MM-JJJJ vor
  • eine neue Eigenschaft ist vorhanden - N ° Film
  • Filmografie ist eine formatierte Eigenschaft, nicht die Standard-JSON-Liste

4. Gson Deserialization

4.1. Einfache Deserialisierung

Durch die Deserialisierung werden JSON-Eingaben in Java-Objekte konvertiert. Um die Ausgabe zu veranschaulichen, implementieren wir die toString () -Methode in beiden Entitätsklassen:

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 + "]"; } ... }

Dann verwenden wir den serialisierten JSON und führen ihn durch die Standard-Gson-Deserialisierung aus:

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

Die Ausgabe sind wir unsere Entitäten, die mit den Daten unserer JSON-Eingabe gefüllt sind:

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

Wie beim einfachen Serializer:

  • Die JSON-Eingabenamen müssen den Java-Entitätsnamen entsprechen, oder sie werden auf null gesetzt.
  • Das Feld dateOfBirth wurde mit dem Standard-Gson- Datumsmuster übersetzt, wobei die Zeitzone ignoriert wurde.

4.2. Benutzerdefinierte Deserialisierung

Durch die Verwendung eines benutzerdefinierten Deserializers können wir das Standardverhalten des Deserializers ändern. In diesem Fall soll das Datum die richtige Zeitzone für dateOfBirth widerspiegeln . Wir verwenden einen benutzerdefinierten ActorGsonDeserializer für die ActorGson- Entität, um dies zu erreichen:

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

Wir haben einen SimpleDateFormat- Parser verwendet, um das Eingabedatum unter Berücksichtigung der Zeitzone zu analysieren.

Beachten Sie, dass wir uns entschieden haben könnten, einfach einen benutzerdefinierten Deserializer nur für das Datum zu schreiben, aber der ActorGsonDeserializer bietet eine detailliertere Ansicht des Deserialisierungsprozesses.

Also note that the Gson approach does not require modifying the ActorGson entity, which is ideal as we may not always have access to the input entity. We use the custom deserializer here:

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

The output is similar to the simple deserializer result, except the date uses correct time zone:

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

5. Jackson Maven Dependency

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

You can get the latest version of Jackson here.

6. Jackson Serialization

6.1. Simple Serialization

Here we will use Jackson to obtain the same serialized content we had with Gson using the following entities. Note that the entity's getters/setters must be public:

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

The output is as follows:

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

Some notes of interest:

  • ObjectMapper is our Jackson serializer/deserializer
  • The output JSON is not formatted
  • By default, Java Date is translated to long value

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:

  • Integriert in alle JAX-RS- (Jersey, Apache CXF, RESTEasy, Restlet) und Spring-Frameworks
  • Umfangreiche Unterstützung für Anmerkungen

Den Code für Gson und Jackson finden Sie auf GitHub.