Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserializing an abstract class in Gson

I have a tree object in JSON format I'm trying to deserialize with Gson. Each node contains its child nodes as fields of object type Node. Node is an interface, which has several concrete class implementations. During the deserialization process, how can I communicate to Gson which concrete class to implement when deserializing the node, if I do not know a priori which type the node belongs to? Each Node has a member field specifying the type. Is there a way to access the field when the object is in serialized form, and somehow communicate the type to Gson?

Thanks!

like image 865
user437480 Avatar asked Sep 02 '10 17:09

user437480


People also ask

How do you deserialize with GSON?

Deserialization in the context of Gson means converting a JSON string to an equivalent Java object. In order to do the deserialization, we need a Gson object and call the function fromJson() and pass two parameters i.e. JSON string and expected java type after parsing is finished.

Is GSON better than Jackson?

Conclusion Both Gson and Jackson are good options for serializing/deserializing JSON data, simple to use and well documented. Advantages of Gson: Simplicity of toJson/fromJson in the simple cases. For deserialization, do not need access to the Java entities.

Is GSON toJson thread safe?

Gson is typically used by first constructing a Gson instance and then invoking toJson(Object) or fromJson(String, Class) methods on it. Gson instances are Thread-safe so you can reuse them freely across multiple threads.

Is GSON fast?

Gson is the slowest at deserializing JSONs. It's almost twice as slow as Moshi and JSONObject and slower than Jackson by more than twice in that regard. Another thing to take note at is Gson is the only library producing larger JSON Strings than the other solutions.


2 Answers

I'd suggest adding a custom JsonDeserializer for Nodes:

Gson gson = new GsonBuilder()     .registerTypeAdapter(Node.class, new NodeDeserializer())     .create(); 

You will be able to access the JsonElement representing the node in the deserializer's method, convert that to a JsonObject, and retrieve the field that specifies the type. You can then create an instance of the correct type of Node based on that.

like image 161
ColinD Avatar answered Sep 17 '22 12:09

ColinD


You will need to register both JSONSerializer and JSONDeserializer. Also you can implement a generic adapter for all your interfaces in the following way:

  • During Serialization : Add a META-info of the actual impl class type.
  • During DeSerialization : Retrieve that meta info and call the JSONDeserailize of that class

Here is the implementation that I have used for myself and works fine.

public class PropertyBasedInterfaceMarshal implements         JsonSerializer<Object>, JsonDeserializer<Object> {      private static final String CLASS_META_KEY = "CLASS_META_KEY";      @Override     public Object deserialize(JsonElement jsonElement, Type type,             JsonDeserializationContext jsonDeserializationContext)             throws JsonParseException {         JsonObject jsonObj = jsonElement.getAsJsonObject();         String className = jsonObj.get(CLASS_META_KEY).getAsString();         try {             Class<?> clz = Class.forName(className);             return jsonDeserializationContext.deserialize(jsonElement, clz);         } catch (ClassNotFoundException e) {             throw new JsonParseException(e);         }     }      @Override     public JsonElement serialize(Object object, Type type,             JsonSerializationContext jsonSerializationContext) {         JsonElement jsonEle = jsonSerializationContext.serialize(object, object.getClass());         jsonEle.getAsJsonObject().addProperty(CLASS_META_KEY,                 object.getClass().getCanonicalName());         return jsonEle;     }  } 

Then you could register this adapter for all your interfaces as follows

Gson gson = new GsonBuilder()         .registerTypeAdapter(IInterfaceOne.class,                 new PropertyBasedInterfaceMarshal())         .registerTypeAdapter(IInterfaceTwo.class,                 new PropertyBasedInterfaceMarshal()).create(); 
like image 35
Guruprasad GV Avatar answered Sep 17 '22 12:09

Guruprasad GV