Deserializing an abstract class in Gson

前端 未结 4 1969
盖世英雄少女心
盖世英雄少女心 2020-11-27 16:10

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

相关标签:
4条回答
  • 2020-11-27 16:26

    You have to use TypeToken class from Google Gson. You will need of course has a generic class T to make it works

    Type fooType = new TypeToken<Foo<Bar>>() {}.getType();
    

    gson.toJson(foo, fooType);

    gson.fromJson(json, fooType);
    
    0 讨论(0)
  • 2020-11-27 16:47

    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();
    
    0 讨论(0)
  • 2020-11-27 16:48

    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.

    0 讨论(0)
  • 2020-11-27 16:49

    As far as I can tell this doesn't work for non-collection types, or more specifically, situations where the concrete type is used to serialize, and the interface type is used to deserialize. That is, if you have a simple class implementing an interface and you serialize the concrete class, then specify the interface to deserialize, you'll end up in an unrecoverable situation.

    In the above example the type adapter is registered against the interface, but when you serialize using the concrete class it will not be used, meaning the CLASS_META_KEY data will never be set.

    If you specify the adapter as a hierarchical adapter (thereby telling gson to use it for all types in the hierarchy), you'll end up in an infinite loop as the serializer will just keep calling itself.

    Anyone know how to serialize from a concrete implementation of an interface, then deserialize using only the interface and an InstanceCreator?

    By default it seems that gson will create the concrete instance, but does not set it's fields.

    Issue is logged here:

    http://code.google.com/p/google-gson/issues/detail?id=411&q=interface

    0 讨论(0)
提交回复
热议问题