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!
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.
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.
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.
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.
I'd suggest adding a custom JsonDeserializer for Node
s:
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.
You will need to register both JSONSerializer and JSONDeserializer. Also you can implement a generic adapter for all your interfaces in the following way:
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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With