11

Suppose to have an abstract class, say A, and two non-abstract subclasses, say A1 and A2. I want to "deserialize" them from a json format by using the GSON library.

E.g. I get an array of A objects.

int n = ...;
A[] list = new A[n];
A[0] = new A1(....);
A[1] = new A2(....);
...

which someone converted to a JSON string as follows:

String json = (new Gson()).toJson(list);

Finally, if I try to deserialize as follows

A[] deserializedList =  (new Gson()).fromJson(json, A[].class);

then I has got an error, since the GSON default deserializer finds an abstract class (i.e. A) and it is not capable of guessing the subclass type.

How can I solve this?

PS: I read about custom deserializer, but I don't understand how to use them in this case.

mat_boy
  • 12,998
  • 22
  • 72
  • 116
  • 7
    Take a look at this http://ovaraksin.blogspot.com.es/2011/05/json-with-gson-and-abstract-classes.html – Axxiss Jun 01 '13 at 12:10
  • 2
    @Axxiss Oh! Ok, so I can't use a simple list, I have to save the object class type in the serialized string. Mhhh! It would be better if the GSON guys will add a method to enabled the auto saving of the class type property! – mat_boy Jun 01 '13 at 12:12
  • 1
    Well, please, post an asnwer related to this question so that I can accept it! – mat_boy Jun 01 '13 at 12:15
  • Well,currently I don't have the time to give a better answer. Probably you should create an answer by yourself taking the link as a starting point ;) – Axxiss Jun 01 '13 at 12:56
  • 1
    @Axxiss Of course, I'm going to do this. My aim was to accept your answer in order to give the points to you! – mat_boy Jun 01 '13 at 13:00
  • After trying the explicit class name like A[] deserializedList = (new Gson()).fromJson(json, A1.class); did u get any error? @mat_boy – MaheshVarma Jun 01 '13 at 13:05

1 Answers1

19

Following the Axxiss link, here follows the answer. A custom serializer/deserializer must be provided.

public class AClassAdapter  implements JsonSerializer<A>, JsonDeserializer<A> {
  @Override
  public JsonElement serialize(A src, Type typeOfSrc, JsonSerializationContext context) {
      JsonObject result = new JsonObject();
      result.add("type", new JsonPrimitive(src.getClass().getSimpleName()));
      result.add("properties", context.serialize(src, src.getClass())); 
      return result;
  }


  @Override
  public A deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
        throws JsonParseException {
    JsonObject jsonObject = json.getAsJsonObject();
    String type = jsonObject.get("type").getAsString();
    JsonElement element = jsonObject.get("properties");

    try {            
        String fullName = typeOfT.getTypeName();
        String packageText = fullName.substring(0, fullName.lastIndexOf(".") + 1);

        return context.deserialize(element, Class.forName(packageText + type));
    } catch (ClassNotFoundException cnfe) {
        throw new JsonParseException("Unknown element type: " + type, cnfe);
    }
  }
}

Then the serialization is done like follows:

GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(A.class, new ATypeAdapter());
String json = gson.create().toJson(list);

and given the json string, the deserialization is:

GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(A.class, new ATypeAdapter());

return gson.create().fromJson(json, A[].class);
the_new_mr
  • 3,476
  • 5
  • 42
  • 57
mat_boy
  • 12,998
  • 22
  • 72
  • 116