GsonでJsonSerializer/JsonDeserializerを使う

Gsonを使ってJavaのObjectとJsonの間をコンバートする際、カスタマイズしたい時があったのでどんなユースケースだったのかとどう書いたかのメモ。 もっと良いやり方がありそうな気がするが作業ログということで。使用しているGson ver: 2.8.1。

Deserialize

JsonからJavaのObjectにマッピングする時。自分は以下のようなユースケースでDeserializerを書いた。 同じ名前のkeyだが中身の型は違う、がObjectとしては同じObjectにコンバートしたい時。

{
"id": 1,
"name": "example"
"info":{
              "name": "john"
              "age": 25
}
}
{
"id": 1,
"name": "example"
"info":{
              "id": 111
              "agelist": [25,26,27]
              "number": 3
}
}

"info"という同じ名前でレスポンスは返ってくるが中身の構成は違うという状況である。 Jsonを受けるObjectはこんな感じで作る。Infoを継承したそれぞれのレスポンスに合わせたObjectを作っていく。

class Response {
int id;
String name;
Info info;
}
class Info{
}

class Profile extends Info{
String name;
int age;
}
class Group extends Info{
int id;
int[] agelist;
int number;
}
public static final JsonDeserializer<Info> JSON_DESERIALIZER = (json, typeOfT, context) -> {

        if (json.getAsJsonObject().get("id") == null) {
            return context.deserialize(json, Profile.class);
        } else {
            return context.deserialize(json, Group.class);
        }
    };

JsonDeserializerを定義しjsonの中身によってどのclassにデシリアライズするかを設定する。

new GsonBuilder().registerTypeAdapter(Info.class,JSON_DESERIALIZER);

Gsonを生成する時にDeserializerを設定する。これでObjectのInfoには中身によってProfileクラスかGroupクラスが入るようになった。

Serialize

JavaのObjectからJsonにコンバートする時。

stackoverflow.com

上のリンクと同様サブクラスがうまくserializeできないことがある。

Gson.toJson(response.info))

上のように直接Infoを指定すると正しくJsonにコンバートされるが、

Gson.toJson(response))

コンバートしたい大元のObejctをいれるとフィールドでもっているinfoは正しくJsonにコンバートされず info:{} という形になってしまった。

したがってDeserialize同様JsonSerializerを書いて解消する。Object型にするとこの問題は解決するのでInfoがクラスがserializeされる時のみObject型にキャストしてSerializeするようにした。

public static final JsonSerializer<Info> JSON_SERIALIZER = (in, type, context) -> context.serialize((Object) in);
new GsonBuilder().registerTypeAdapter(Info.class,JSON_SERIALIZER);