デフォルトでは、Gson は必要な JSON 文字列を提供しません。列挙型の特定のシリアライザーで Gson をカスタマイズする必要があります。ここにあります:
package stackoverflow.questions.q19715374;
import java.lang.reflect.Type;
import java.util.*;
import com.google.gson.*;
public class CustomSerializer implements JsonSerializer<Error> {
@Override
public JsonElement serialize(Error error, Type typeOfSrc,
JsonSerializationContext context) {
if (error == null)
return null;
else {
JsonObject jo = new JsonObject();
jo.add("code", new JsonPrimitive(error.code));
jo.add("message", new JsonPrimitive(error.message));
return jo;
}
}
}
で、この使い方。
public static void main(String[] args) {
A a = new A();
a.id="XX";
a.error = Error.INVALID;
Gson defaultGson = new Gson();
System.out.println("With default Gson: "+defaultGson.toJson(a));
GsonBuilder gb = new GsonBuilder();
gb.registerTypeAdapter(Error.class, new CustomSerializer());
Gson customGson = gb.create();
System.out.println("With custom Gson: "+ customGson.toJson(a));
}
これは実行結果です:
With default Gson: {"id":"XX","error":"INVALID"}
With custom Gson: {"id":"XX","error":{"code":0,"message":"Does not exist"}}
投稿した JSON は無効であることに注意してください。コロンが必要です。
なんで?(必要に応じてスキップできます)
また、「プロパティ」ではなく、列挙値の名前をシリアル化する理由も尋ねました。答えは簡単です。Gson のデフォルトの動作はEnumTypeAdapter
. このクラスは、enum のシリアル化を減らして enum 値の名前を出力し、逆シリアル化を減らしてその名前から enum 値を取得します。
したがって、列挙型のプロパティをシリアル化する場合は、お見せしたカスタム シリアライザーを使用する必要があります。カスタム シリアライザーで生成した Json からデシリアライズするには、コード (この場合) を列挙値にマップするカスタム デシリアライザーも必要です。
編集
この場合のデシリアライザーを作成する場合は、次のError
ように変更する必要があります。
public enum Error {
INVALID(0, "Does not exist"), SERVER_ERROR(1, "Server error");
int code;
String message;
private Error(int code, String message) {
this.code = code;
this.message = message;
}
static private Map<Integer, Error> map;
static {
map = new TreeMap<Integer, Error>();
map.put(INVALID.code, INVALID);
map.put(SERVER_ERROR.code, SERVER_ERROR);
}
public static Error getByCode(int code) {
return map.get(code);
}
}
そして、デシリアライザーは非常に単純です。
public class CustomDeserializer implements JsonDeserializer<Error> {
public Error deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
if (json == null)
return null;
else {
JsonElement e = json.getAsJsonObject().get("code");
if (e == null || e instanceof JsonNull)
return null;
int code = e.getAsInt();
return Error.getByCode(code);
}
}
}
これは、Gson を使用するように構成する方法です。
GsonBuilder gb2 = new GsonBuilder();
gb2.registerTypeAdapter(Error.class, new CustomDeserializer());
Gson customGson2 = gb2.create();
String jsonTest1 = "{\"id\":\"AA\",\"error\":{\"code\":1}}";
String jsonTest2 = "{\"id\":\"BB\"}";
String jsonTest3 = "{\"id\":\"CC\",\"error\":{\"code\":42, \"message\":\"This is the answer\"}}";
System.out.println("Deserialize test 1: "+ customGson2.fromJson(jsonTest1, A.class));
System.out.println("Deserialize test 2: "+ customGson2.fromJson(jsonTest2, A.class));
System.out.println("Deserialize test 3: "+ customGson2.fromJson(jsonTest3, A.class));
これにより、次の結果が得られます。
Deserialize test 1: A [id=AA, error=SERVER_ERROR]
Deserialize test 2: A [id=BB, error=null]
Deserialize test 3: A [id=CC, error=null]
コードは列挙型の一意の (代替) 識別子であると想定したため、メッセージ フィールドは省略できます。コードが null または見つからない場合は、 null が返されることに注意してくださいError
。
最後に、例は異なりますが、デシリアライザーとシリアライザーの両方をビルダーに追加できます。