GSONでデータクラスのList・MapのJSON変換

やりたいこと

今回やりたいことは以下になります。

  • 独自のデータクラス(バリューオブジェクト)のListやMapをJSON形式に変換したい
  • 作成したJSONは単純にデータクラスが保持している値を出力したい

StringのListやMapをJSONにしたときと同じものをバリューオブジェクトで取得したいという感じです。

結論

先に結論を記載しておきます。他の方法もあるもしれませんが、以下の方法で実施できました。

  • JsonSerializerを実装する
  • Gsonの生成時に実装したJsonSerializerを指定する

説明

デフォルトのGsonでJSON変換

以下のようなバリューオブジェクトを用意します。アクセッサなどは省略しています。

public class ValueObject {
    private String value;
}

まずは普通にGSONを使ってみます。

    @Test
    public void toJsonTest() {
        List<ValueObject> list = Arrays.asList(
                new ValueObject("vo1"), new ValueObject("vo2"), new ValueObject("vo3")
        );

        Gson gson = new Gson();
        String json = gson.toJson(list);
        System.out.println(json);
    }
    @Test
    public void toJsonTest() {
        Map<String, ValueObject> map = new LinkedHashMap<>();
        map.put("k1", new ValueObject("vo1"));
        map.put("k2", new ValueObject("vo2"));
        map.put("k3", new ValueObject("vo3"));
        Gson gson = new Gson();
        String json = gson.toJson(map);
        System.out.println(json);
    }

上記を実行すると以下の結果が得られます。

[{"value":"vo1"},{"value":"vo2"},{"value":"vo3"}]
{"k1":{"value":"vo1"},"k2":{"value":"vo2"},"k3":{"value":"vo3"}}

GSONのデフォルトの動きだとフィールド名(valueという文字列)が一緒に出力されてしまいます。

欲しいものは以下のような形なので、デフォルトのままだと期待のものが得られないことが分かります。

["vo1","vo2","vo3"]
{"k1":"vo1","k2":"vo2","k3":"vo3"}

JsonSerializerを用いてJSON変換

GsonのtoJsonメソッドをデバッグしてみるとJSONの変換の際にfactoriesというフィールドに登録されているTypeAdapterを用いていることが分かりました。

さらに調べてみるとGsonBuilderのregisterTypeAdapterでTypeAdapterを登録できることも分かりました。また、registerTypeAdapterでの登録の際にはいくつかのインターフェースや抽象クラスを受け付けていることも分かりました。

今回は、その中のJsonSerializerインターフェースを実装してみます。これは今回はオブジェクトからJSONへの変換(シリアライズ)だけが欲しかったためです。もし、JSON(文字列)からオブジェクトへの変換(デシリアライズ)も必要だった場合は、他のインターフェースや抽象クラスの実装という形になると思います。

実際に作成したものが以下になります。どこに置いても良いですが、今回は対象のデータクラス(バリューオブジェクト)のstaticフィールドに定義することにしました。

public static final JsonSerializer<ValueObject> JSON_SERIALIZER = new JsonSerializer<ValueObject>() {
    @Override
    public JsonElement serialize(ValueObject src, Type typeOfSrc, JsonSerializationContext context) {
        return context.serialize(src.value);
    }
};

上記を使ってJSONを生成してみます。

    @Test
    public void toJsonFromJsonSerializerTest() {
        List<ValueObject> list = Arrays.asList(
                new ValueObject("vo1"), new ValueObject("vo2"), new ValueObject("vo3")
        );
        Gson gson = new GsonBuilder().registerTypeAdapter(
                ValueObject.class, ValueObject.JSON_SERIALIZER).create();
        String json = gson.toJson(list);
        System.out.println(json);
    }
    @Test
    public void toJsonFromJsonSerializerTest() {
        Map<String, ValueObject> map = new LinkedHashMap<>();
        map.put("k1", new ValueObject("vo1"));
        map.put("k2", new ValueObject("vo2"));
        map.put("k3", new ValueObject("vo3"));
        Gson gson = new GsonBuilder().registerTypeAdapter(
                ValueObject.class, ValueObject.JSON_SERIALIZER).create();
        String json = gson.toJson(map);
        System.out.println(json);
    }

上記を実行すると以下の結果が得られます。

["vo1","vo2","vo3"]
{"k1":"vo1","k2":"vo2","k3":"vo3"}

これで欲しいものが得ることができました。

補足

今回の実装だと作成したデータクラスの数だけJsonSerializerの実装が必要になりそうな感じです。それだと大変なので共通的に使える方法がないかについて別途検討してみました。以下はその記事になります。興味がある方は見てもらえればと思います。

作成物

今回の作成物(サンプル)は以下に置いています。

https://github.com/masaki-code/java/tree/master/gson/src/main/java/net/masaki_blog/gson/vo

コメント

タイトルとURLをコピーしました