ジャクソン対Gson

ジャクソンvs Gson

1. 前書き

この記事では、JSONデータをJavaオブジェクトにシリアル化および逆シリアル化するためのGsonおよびJacksonAPIを比較します。

GsonとJacksonは、JavaのJSONデータバインディングサポートを提供する完全なライブラリです。 それぞれが積極的に開発されたオープンソースプロジェクトであり、複雑なデータ型の処理とJavaジェネリックのサポートを提供します。

また、ほとんどの場合、両方のライブラリはエンティティクラスを変更せずにエンティティに逆シリアル化できます。これは、開発者がエンティティのソースコードにアクセスできない場合に重要です。

2. GsonMavenの依存関係


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

Gsonhereの最新バージョンを入手できます。

3. Gsonシリアル化

シリアル化は、JavaオブジェクトをJSON出力に変換します。 次のエンティティを検討してください。

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. 単純なシリアル化

Javaから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);

これは次のようになります。

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

デフォルトでは:

  • null値がないため、すべてのプロパティがシリアル化されます

  • dateOfBirthフィールドは、デフォルトのGson日付パターンで変換されました

  • 出力はフォーマットされておらず、JSONプロパティ名はJavaエンティティに対応しています

3.2. カスタムシリアル化

カスタムシリアライザーを使用すると、標準の動作を変更できます。 HTMLを使用した出力フォーマッターの導入、null値の処理、出力からのプロパティの除外、または新しい出力の追加を行うことができます。

ActorGsonSerializerは、ActorGson要素のJSONコードの生成を変更します。

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

directorプロパティを除外するために、検討するプロパティに@Exposeアノテーションが使用されます。

public class MovieWithNullValue {

    @Expose
    private String imdbId;
    private String director;

    @Expose
    private List actors;
}

これで、GsonBuilderクラスを使用してGsonオブジェクトの作成を進めることができます。

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

結果は次のとおりです。

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

次のことに注意してください。

  • 出力はフォーマットされます

  • 一部のプロパティ名が変更され、HTMLが含まれています

  • null値が含まれ、directorフィールドは省略されます

  • Datedd-MM-yyyy形式になりました

  • 新しいプロパティが存在します–N° Film

  • filmographyはフォーマットされたプロパティであり、デフォルトのJSONリストではありません

4. Gsonの逆シリアル化

4.1. 単純な逆シリアル化

デシリアライゼーションは、JSON入力をJavaオブジェクトに変換します。 出力を説明するために、両方のエンティティクラスにtoString()メソッドを実装します。

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

次に、シリアル化されたJSONを利用し、標準のGson逆シリアル化を実行します。

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

出力は、JSON入力からのデータが入力されたエンティティです。

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

単純なシリアライザーの場合と同様に:

  • JSON入力名はJavaエンティティ名と一致する必要があります。そうでない場合は、nullに設定されます。

  • dateOfBirthフィールドは、タイムゾーンを無視して、デフォルトのGson日付パターンで変換されました。

4.2. カスタム逆シリアル化

カスタムデシリアライザーを使用すると、標準のデシリアライザーの動作を変更できます。 この場合、日付にdateOfBirthの正しいタイムゾーンを反映させる必要があります。 これを実現するために、ActorGsonエンティティでカスタムActorGsonDeserializerを使用します。

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

SimpleDateFormatパーサーを使用して、タイムゾーンを考慮して入力日付を解析しました。

日付のみのカスタムデシリアライザーを作成することもできますが、ActorGsonDeserializerはデシリアライズプロセスのより詳細なビューを提供することに注意してください。

また、Gsonアプローチでは、ActorGsonエンティティを変更する必要がないことにも注意してください。これは、入力エンティティに常にアクセスできるとは限らないため、理想的です。 ここでは、カスタムデシリアライザーを使用します。

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

出力は、日付が正しいタイムゾーンを使用することを除いて、単純なデシリアライザーの結果に似ています。

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

5. JacksonMavenの依存関係


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

ジャクソンhereの最新バージョンを入手できます。

6. ジャクソンシリアル化

6.1. 単純なシリアル化

ここでは、Jacksonを使用して、次のエンティティを使用してGsonで持っていたのと同じシリアル化されたコンテンツを取得します。 エンティティのゲッター/セッターは公開されている必要があることに注意してください。

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

出力は次のとおりです。

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

興味のあるメモ:

  • ObjectMapperはJacksonシリアライザー/デシリアライザーです

  • 出力JSONはフォーマットされていません

  • デフォルトでは、Java日付はlong値に変換されます

6.2. カスタムシリアル化

エンティティのStdSerializerを拡張することにより、ActorJackson要素生成用のJacksonシリアライザーを作成できます。 繰り返しますが、エンティティのゲッター/セッターはパブリックでなければなりません。

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

directorフィールドを無視できるようにMovieエンティティを作成します。

public class MovieWithNullValue {

    private String imdbId;

    @JsonIgnore
    private String director;

    private List actors;

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

これで、カスタムObjectMapperの作成とセットアップに進むことができます。

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

出力は、null値を処理し、日付をフォーマットし、directorフィールドを除外し、の新しい出力を表示するフォーマット済みJSONです。

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

7. ジャクソンの逆シリアル化

7.1. 単純な逆シリアル化

出力を説明するために、両方のJacksonエンティティクラスにtoString()メソッドを実装します。

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

次に、シリアル化されたJSONを利用して、ジャクソンの逆シリアル化を実行します。

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

出力は、JSON入力からのデータが入力されたエンティティです。

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

単純なシリアライザーの場合と同様に:

  • JSON入力名はJavaエンティティ名に対応している必要があります。そうでない場合は、null,に設定されます。

  • dateOfBirthフィールドは、タイムゾーンを無視して、デフォルトのジャクソン日付パターンで変換されました。

7.2. カスタム逆シリアル化

カスタムデシリアライザーを使用すると、標準のデシリアライザーの動作を変更できます。

この場合、日付にdateOfBirth,の正しいタイムゾーンを反映させたいので、JacksonObjectMapperにDateFormatterを追加します。

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

出力には、正しいタイムゾーンと日付が反映されます。

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

このソリューションはクリーンでシンプルです。

または、ActorJacksonクラスのカスタムデシリアライザーを作成し、このモジュールをObjectMapperに登録し、ActorJacksonエンティティの@JsonDeserializeアノテーションを使用して日付をデシリアライズすることもできます。

このアプローチの欠点は、エンティティを変更する必要があることです。これは、入力エンティティクラスにアクセスできない場合には理想的ではない可能性があります。

8. 結論

GsonとJacksonは、JSONデータのシリアル化/逆シリアル化に適したオプションであり、使いやすく、十分に文書化されています。

Gsonの利点:

  • 単純な場合のtoJson /fromJsonの単純さ

  • 逆シリアル化の場合、Javaエンティティにアクセスする必要はありません

ジャクソンの利点:

  • すべてのJAX-RS(ジャージー、Apache CXF、RESTEasy、Restlet)、およびSpringフレームワークに組み込まれています

  • 広範な注釈のサポート

GsonJacksonのコードはGitHubにあります。