Jackson vs Gson

Jackson gegen Gson

1. Einführung

In diesem Artikel werden die APIsGson undJackson 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 für Java Generics anbieten.

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}

Sie können die neueste Version von Gsonhere erhalten.

3. Gson-Serialisierung

Die Serialisierung konvertiert Java-Objekte in die JSON-Ausgabe. 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 keinenull-Werte haben

  • Das FelddateOfBirthwurde mit dem Standard-Gson-Datumsmuster übersetzt

  • Die Ausgabe ist nicht formatiert und die JSON-Eigenschaftsnamen entsprechen den Java-Entitäten

3.2. Benutzerdefinierte Serialisierung

Mit einem benutzerdefinierten Serializer können wir das Standardverhalten ändern. Wir können einen Ausgabeformatierer mit HTML einführen,null-Werte verarbeiten, Eigenschaften von der Ausgabe ausschließen oder eine neue Ausgabe hinzufügen.

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

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 Eigenschaftdirectorauszuschließen, wird die Annotation@Exposefü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 KlasseGsonBuilderfortfahren:

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 das Folgende:

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

Beachte das:

  • Die Ausgabe wird formatiert

  • Einige Eigenschaftsnamen werden geändert und enthalten HTML

  • null Werte sind enthalten und das Felddirector wird weggelassen

  • Date hat jetzt das Formatdd-MM-yyyy

  • eine neue Eigenschaft ist vorhanden -N° Film

  • Filmografie ist eine formatierte Eigenschaft, nicht die Standard-JSON-Liste

4. Gson Deserialization

4.1. Einfache Deserialisierung

Die Deserialisierung konvertiert JSON-Eingaben in Java-Objekte. Um die Ausgabe zu veranschaulichen, implementieren wir dietoString()-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 standardmäßige Gson-Deserialisierung:

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 aus 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 mit den Java-Entitätsnamen übereinstimmen oder auf null gesetzt sein.

  • Das FelddateOfBirthwurde mit dem Standard-Gson-Datumsmuster übersetzt, wobei die Zeitzone ignoriert wurde.

4.2. Benutzerdefinierte Deserialisierung

Durch die Verwendung eines benutzerdefinierten Deserialisierers können wir das Standardverhalten des Deserialisierers ändern. In diesem Fall soll das Datum die korrekte Zeitzone fürdateOfBirth widerspiegeln. Wir verwenden ein benutzerdefiniertesActorGsonDeserializer für die EntitätActorGson, 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 einenSimpleDateFormat-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, aberActorGsonDeserializer bietet eine detailliertere Ansicht des Deserialisierungsprozesses.

Beachten Sie auch, dass für den Gson-Ansatz keine Änderung derActorGson-Entität erforderlich ist. Dies ist ideal, da wir möglicherweise nicht immer Zugriff auf die Eingabeentität haben. Wir verwenden hier den benutzerdefinierten Deserializer:

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

Die Ausgabe ähnelt dem einfachen Deserializer-Ergebnis, außer dass das Datum die richtige Zeitzone verwendet:

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 Abhängigkeit


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

Sie können die neueste Version von Jacksonhere erhalten.

6. Jackson-Serialisierung

6.1. Einfache Serialisierung

Hier verwenden wir Jackson, um denselben serialisierten Inhalt zu erhalten, den wir mit Gson mithilfe der folgenden Entitäten hatten. Beachten Sie, dass die Getter / Setter der Entität öffentlich sein müssen:

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

Die Ausgabe ist wie folgt:

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

Einige Anmerkungen von Interesse:

  • ObjectMapper ist unser Jackson Serializer / Deserializer

  • Der Ausgabe-JSON ist nicht formatiert

  • Standardmäßig wird Java Date in den Wert vonlongübersetzt

6.2. Benutzerdefinierte Serialisierung

Wir können einen Jackson-Serializer für die Elementgenerierung vonActorJacksonerstellen, indem wir StdSerializer für unsere Entität erweitern. Beachten Sie erneut, dass die Getter / Setter der Entität öffentlich sein müssen:

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

Wir erstellen eine Movie-Entität, damit das Felddirectorignoriert werden kann:

public class MovieWithNullValue {

    private String imdbId;

    @JsonIgnore
    private String director;

    private List actors;

    // required getters and setters, default constructor
    // and field constructor details omitted
}

Jetzt können wir mit der Erstellung und Einrichtung eines benutzerdefiniertenObjectMapperfortfahren:

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

Die Ausgabe ist JSON-formatiert, das die Werte vonnullverarbeitet, das Datum formatiert, das Felddirectorausschließt und die neue Ausgabe von anzeigt:

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

7. Jackson Deserialization

7.1. Einfache Deserialisierung

Um die Ausgabe zu veranschaulichen, implementieren wir dietoString()-Methode in beiden Jackson-Entitätsklassen:

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

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

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

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

Movie [imdbId=tt0472043, director=null, actors=[ActorJackson
  [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 sind aufnull, festgelegt

  • Das Feld vondateOfBirthwurde mit dem Standard-Jackson-Datumsmuster übersetzt, wobei die Zeitzone ignoriert wurde.

7.2. Benutzerdefinierte Deserialisierung

Durch die Verwendung eines benutzerdefinierten Deserialisierers können wir das Standardverhalten des Deserialisierers ändern.

In diesem Fall soll das Datum die korrekte Zeitzone fürdateOfBirth, widerspiegeln, daher fügen wir unseren JacksonObjectMapper einen DateFormatter hinzu:

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

Die Ausgabe spiegelt die richtige Zeitzone mit dem Datum wider:

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

Diese Lösung ist sauber und einfach.

Alternativ hätten wir einen benutzerdefinierten Deserializer für die KlasseActorJacksonerstellen, dieses Modul beiObjectMapperregistrieren und das Datum mithilfe der Annotation@JsonDeserializefür die EntitätActorJacksondeserialisieren können.

Der Nachteil dieses Ansatzes ist die Notwendigkeit, die Entität zu ändern. Dies ist möglicherweise nicht ideal für Fälle, in denen wir keinen Zugriff auf die eingegebenen Entitätsklassen haben.

8. Fazit

Sowohl Gson als auch Jackson sind gute Optionen zum Serialisieren / Deserialisieren von JSON-Daten, einfach zu verwenden und gut dokumentiert.

Vorteile von Gson:

  • Einfachheit vontoJson /fromJson in den einfachen Fällen

  • Für die Deserialisierung benötigen Sie keinen Zugriff auf die Java-Entitäten

Vorteile von Jackson:

  • Integriert in alle JAX-RS- (Jersey, Apache CXF, RESTEasy, Restlet) und Spring-Frameworks

  • Umfassende Unterstützung für Anmerkungen

Sie finden den Code fürGson undJackson auf GitHub.