1. 序章

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

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

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

2. GsonMavenの依存関係

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>${gson.version}</version>
</dependency>

Gsonの最新バージョンはこちらで入手できます。

3. Gsonシリアル化

シリアル化は、JavaオブジェクトをJSON出力に変換します。 次のエンティティについて考えてみます。

public class ActorGson {
    private String imdbId;
    private Date dateOfBirth;
    private List<String> filmography;
    
    // getters and setters, default constructor and field constructor omitted
}

public class Movie {
    private String imdbId;
    private String director;
    private List<ActorGson> 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<ActorGson> {
    private SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");
     
    @Override
    public JsonElement serialize(ActorGson actor, Type type,
        JsonSerializationContext jsonSerializationContext) {
        
        JsonObject actorJsonObj = new JsonObject();
        
        actorJsonObj.addProperty("<strong>IMDB Code</strong>", actor.getImdbId());
        
        actorJsonObj.addProperty("<strong>Date Of Birth</strong>", 
          actor.getDateOfBirth() != null ? 
          sdf.format(actor.getDateOfBirth()) : null);
        
        actorJsonObj.addProperty("<strong>N° Film:</strong> ",  
          actor.getFilmography()  != null ?  
          actor.getFilmography().size() : null);
       
        actorJsonObj.addProperty("filmography", actor.getFilmography() != null ? 
          convertFilmography(actor.getFilmography()) : null);
        
        return actorJsonObj;
    }
 
    private String convertFilmography(List<String> filmography) {
        return filmography.stream()
          .collect(Collectors.joining("-"));
    }
}

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

public class MovieWithNullValue {
    
    @Expose
    private String imdbId;
    private String director;
    
    @Expose
    private List<ActorGson> 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": [
    {
      "<strong>IMDB Code</strong>": "nm2199632",
      "<strong>Date Of Birth</strong>": "21-09-1982",
      "<strong>N° Film:</strong> ": 3,
      "filmography": "Apocalypto-Beatdown-Wind Walkers"
    }
  ]
}

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

  • 出力はフォーマットされます
  • 一部のプロパティ名が変更され、HTMLが含まれています
  • null 値が含まれ、ディレクターフィールドは省略されます
  • Datedd-MM-yyyy形式になりました
  • 新しいプロパティが存在します–N°フィルム
  • フィルモグラフィはフォーマットされたプロパティであり、デフォルトの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<ActorGson> {

    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<String> filmList = new ArrayList<String>();
        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. Jackson Maven Dependency

<dependency> 
    <groupId>com.fasterxml.jackson.core</groupId> 
    <artifactId>jackson-databind</artifactId>   
    <version>${jackson.version}</version> 
</dependency>

ジャクソンの最新バージョンはこちらで入手できます。

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

6.1. 単純なシリアル化

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

public class ActorJackson {
    private String imdbId;
    private Date dateOfBirth;
    private List<String> filmography;
    
    // required getters and setters, default constructor 
    // and field constructor details omitted
}

public class Movie {
    private String imdbId;
    private String director;
    private List<ActorJackson> 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日付は longvalueに変換されます

6.2. カスタムシリアル化

エンティティのStdSerializerを拡張することにより、ActorJackson要素生成用のJacksonシリアライザーを作成できます。 ここでも、エンティティのゲッター/セッターは公開されている必要があることに注意してください。

public class ActorJacksonSerializer extends StdSerializer<ActorJackson> {

    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<ActorJackson> 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を利用して、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);

出力は私たちのエンティティであり、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、の正しいタイムゾーンを反映させたいので、Jackson ObjectMapperに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 に登録し、@JsonDeserializeアノテーションを使用して日付をデシリアライズすることもできます。 ActorJacksonエンティティ。

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

8. 結論

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

Gsonの利点:

  • 単純なケースでのtoJson / fromJsonの単純さ
  • デシリアライズの場合、Javaエンティティにアクセスする必要はありません

ジャクソンの利点:

  • すべてのJAX-RS(Jersey、Apache CXF、RESTEasy、Restlet)、およびSpringフレームワークに組み込まれています
  • 広範な注釈のサポート

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