By default, Gson will not give you the JSON string you want, you have to customize Gson with a specific serializer for you enum. Here it is:
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;
}
}
}
And this how to use it.
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));
}
This is execution result:
With default Gson: {"id":"XX","error":"INVALID"}
With custom Gson: {"id":"XX","error":{"code":0,"message":"Does not exist"}}
Pay attention that JSON you posted is invalid, a colon is needed.
Why? (you can skip if you want)
You asked also why you serialize the name of the enum value instead of its "properties". The answer is simple, default behavior for Gson is to use the EnumTypeAdapter
. This class reduce the serialization of enum to printout the name of the enum value and the deserialization to get the enum value from its name.
So if you want to serialize the properties of your enum, you have to use the custom serializer I showed you. To deserialize from the Json you produce with the custom serializer, you need also a custom deserializer that maps code (in this case) to your enum value.
Edit
If you want to write a deserializer for this case, you need to change Error
like this:
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);
}
}
and then the deserializer is very simple.
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);
}
}
}
and this is how to configure Gson to use it:
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));
This get you this result:
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]
I assumed that code is a unique (alternative) identifier for your enum, so message field can be omitted. Note that is code is null or not found, you get an null Error
.
A final note, although the example is different, you can add both deserializer and serializer to your builder.