Jackson vs Gson

Jackson vs Gson

1. introduction

Dans cet article, nous comparerons les APIGson etJackson pour la sérialisation et la désérialisation des données JSON en objets Java et vice-versa.

Gson et Jackson sont des bibliothèques complètes offrant un support de liaison de données JSON pour Java. Il s’agit de projets open source activement développés qui permettent de gérer des types de données complexes et de prendre en charge les génériques Java.

Et dans la plupart des cas, les deux bibliothèques peuvent se désérialiser en une entité sans modifier une classe d'entité, ce qui est important dans les cas où un développeur n'a pas accès au code source de l'entité.

2. Dépendance de Gson Maven


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

Vous pouvez obtenir la dernière version de Gsonhere.

3. Sérialisation Gson

La sérialisation convertit les objets Java en sortie JSON. Considérons les entités suivantes:

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. Sérialisation simple

Commençons par un exemple de sérialisation Java vers 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);

Cela se traduira par:

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

Par défaut:

  • Toutes les propriétés sont sérialisées car elles n'ont pas de valeursnull

  • Le champdateOfBirth a été traduit avec le modèle de date Gson par défaut

  • La sortie n'est pas formatée et les noms de propriété JSON correspondent aux entités Java.

3.2. Sérialisation personnalisée

L'utilisation d'un sérialiseur personnalisé nous permet de modifier le comportement standard. Nous pouvons introduire un formateur de sortie avec HTML, gérer les valeursnull, exclure des propriétés de la sortie ou ajouter une nouvelle sortie.

ActorGsonSerializer modifie la génération du code JSON pour l'élémentActorGson:

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

Afin d'exclure la propriétédirector, l'annotation@Expose est utilisée pour les propriétés que nous voulons considérer:

public class MovieWithNullValue {

    @Expose
    private String imdbId;
    private String director;

    @Expose
    private List actors;
}

Nous pouvons maintenant procéder à la création d'objets Gson en utilisant la classeGsonBuilder:

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

Le résultat est le suivant:

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

Remarquerez que:

  • la sortie est formatée

  • certains noms de propriétés sont modifiés et contiennent du HTML

  • Les valeursnull sont incluses et le champdirector est omis

  • Date est maintenant au formatdd-MM-yyyy

  • une nouvelle propriété est présente -N° Film

  • la filmographie est une propriété formatée, pas la liste JSON par défaut

4. Désérialisation Gson

4.1. Désérialisation simple

La désérialisation convertit les entrées JSON en objets Java. Pour illustrer la sortie, nous implémentons la méthodetoString() dans les deux classes d'entités:

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

Nous utilisons ensuite le JSON sérialisé et le traitons via la désérialisation 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();

La sortie est nous nos entités, remplies avec les données de notre entrée JSON:

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

Comme ce fut le cas avec le sérialiseur simple:

  • les noms d'entrée JSON doivent correspondre aux noms d'entité Java ou ils sont définis sur null.

  • Le champdateOfBirth a été traduit avec le modèle de date Gson par défaut, ignorant le fuseau horaire.

4.2. Désérialisation personnalisée

L'utilisation d'un désérialiseur personnalisé nous permet de modifier le comportement standard du désérialiseur. Dans ce cas, nous voulons que la date reflète le fuseau horaire correct pourdateOfBirth. Nous utilisons unActorGsonDeserializer personnalisé sur l'entitéActorGson pour y parvenir:

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

Nous avons utilisé un analyseurSimpleDateFormat pour analyser la date d'entrée, en tenant compte du fuseau horaire.

Notez que nous aurions pu décider d'écrire simplement un désérialiseur personnalisé pour uniquement la Date, mais leActorGsonDeserializer offre une vue plus détaillée du processus de désérialisation.

Notez également que l'approche Gson ne nécessite pas de modifier l'entitéActorGson, ce qui est idéal car nous n'avons pas toujours accès à l'entité d'entrée. Nous utilisons le désérialiseur personnalisé ici:

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

La sortie est similaire au résultat du désérialiseur simple, sauf que la date utilise le fuseau horaire correct:

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

5. Dépendance de Jackson Maven


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

Vous pouvez obtenir la dernière version de Jacksonhere.

6. Sérialisation Jackson

6.1. Sérialisation simple

Ici, nous allons utiliser Jackson pour obtenir le même contenu sérialisé que nous avions avec Gson en utilisant les entités suivantes. Notez que les getters / setters de l'entité doivent être publics:

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

La sortie est la suivante:

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

Quelques notes d'intérêt:

  • ObjectMapper est notre sérialiseur / désérialiseur Jackson

  • Le JSON de sortie n'est pas formaté

  • Par défaut, Java Date est converti en valeurlong

6.2. Sérialisation personnalisée

Nous pouvons créer un sérialiseur Jackson pour la génération d'élémentActorJackson en étendant StdSerializer pour notre entité. Encore une fois, notez que l'entité qui obtient / qui établit doit être publique:

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

Nous créons une entité Movie pour permettre d'ignorer le champdirector:

public class MovieWithNullValue {

    private String imdbId;

    @JsonIgnore
    private String director;

    private List actors;

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

Nous pouvons maintenant procéder à une création et une configuration personnalisées deObjectMapper:

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

La sortie est au format JSON qui gère les valeursnull, met en forme la date, exclut le champdirector et affiche la nouvelle sortie de:

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

7. Désérialisation Jackson

7.1. Désérialisation simple

Pour illustrer la sortie, nous implémentons la méthodetoString() dans les deux classes d'entités Jackson:

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

Nous utilisons ensuite le JSON sérialisé et le traitons via la désérialisation de Jackson:

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

La sortie est nous nos entités, remplies avec les données de notre entrée JSON:

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

Comme ce fut le cas avec le sérialiseur simple:

  • les noms d'entrée JSON doivent correspondre aux noms d'entités Java, ou ils sont définis surnull,

  • Le champdateOfBirth a été traduit avec le modèle de date Jackson par défaut, ignorant le fuseau horaire.

7.2. Désérialisation personnalisée

L'utilisation d'un désérialiseur personnalisé nous permet de modifier le comportement standard du désérialiseur.

Dans ce cas, nous voulons que la date reflète le fuseau horaire correct pourdateOfBirth, donc nous ajoutons un DateFormatter à nos JacksonObjectMapper:

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

La sortie reflète le fuseau horaire correct avec la 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]]]]

Cette solution est propre et simple.

Sinon, nous aurions pu créer un désérialiseur personnalisé pour la classeActorJackson, enregistrer ce module avec nosObjectMapper et désérialiser la date en utilisant l'annotation@JsonDeserialize sur l'entitéActorJackson.

L'inconvénient de cette approche est la nécessité de modifier l'entité, ce qui peut ne pas être idéal pour les cas où nous n'avons pas accès aux classes d'entités d'entrée.

8. Conclusion

Gson et Jackson sont tous deux de bonnes options pour la sérialisation / désérialisation des données JSON, simples à utiliser et bien documentées.

Avantages de Gson:

  • Simplicité detoJson /fromJson dans les cas simples

  • Pour la désérialisation, vous n'avez pas besoin d'accéder aux entités Java.

Avantages de Jackson:

  • Intégré à tous les frameworks JAX-RS (Jersey, Apache CXF, RESTEasy, Restlet) et Spring

  • Prise en charge étendue des annotations

Vous pouvez trouver le code pourGson etJackson sur GitHub.