18 votos

Deserializar una clase abstracta en Gson

Tengo un árbol de objetos en formato JSON estoy tratando de deserializar con Gson. Cada nodo contiene sus nodos hijos como campos de tipo de objeto de Nodo. Nodo es un interfaz, que tiene varias de hormigón implementaciones de clase. Durante el proceso de deserialización, ¿cómo me puedo comunicar para Gson que clase concreta de llevar a cabo cuando deserializar el nodo, si no sé a priori que el tipo de nodo pertenece? Cada Nodo tiene un campo de miembro de especificar el tipo. Hay una manera de acceder al campo cuando el objeto está en forma serializada, y de alguna manera de comunicar el tipo de Gson?

Gracias!

20voto

ColinD Puntos 48573

Me gustaría sugerir la adición de una costumbre JsonDeserializer para Nodes:

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

Usted será capaz de acceder a la JsonElement que representa el nodo en el deserializer del método, de convertir a un JsonObject, y recuperar el campo que especifica el tipo. A continuación, puede crear una instancia del tipo correcto de Node basado en eso.

9voto

Guruprasad GV Puntos 119

Usted tendrá que registrarse tanto JSONSerializer y JSONDeserializer. También se puede implementar un adaptador genérico para todas las interfaces de la siguiente manera:

  • Durante la Serialización : Añadir un META-info de la real impl tipo de clase.
  • Durante la Deserialización : Recuperar esa meta info y llamar a la JSONDeserailize de esa clase

Aquí está la aplicación que he utilizado para mi y funciona bien.

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;
    }

}

Entonces usted podría registrar este adaptador para todas las interfaces de la siguiente manera

Gson gson = new GsonBuilder()
        .registerTypeAdapter(IInterfaceOne.class,
                new PropertyBasedInterfaceMarshal())
        .registerTypeAdapter(IInterfaceTwo.class,
                new PropertyBasedInterfaceMarshal()).create();

1voto

Jason Puntos 942

Como lo que puedo decir que esto no funciona para los no-tipos de colección, o más específicamente, situaciones en las que el hormigón se utiliza el tipo de serializar, y el tipo de interfaz se utiliza para deserializar. Es decir, si usted tiene una simple clase que implementa una interfaz y serializar la clase concreta, a continuación, especificar la interfaz para deserializar, usted va a terminar en una situación irrecuperable.

En el ejemplo anterior, el tipo de adaptador que está inscrita en contra de la interfaz, pero cuando se serializa el uso de la clase concreta no va a ser utilizado, es decir, la CLASS_META_KEY datos nunca serán conjunto.

Si se especifica el adaptador jerárquico adaptador (y así diciendo gson a usar para todos los tipos de la jerarquía), que va a terminar en un bucle infinito como el serializador seguirá llamándose a sí mismo.

Alguien sabe cómo serializar de una implementación concreta de una interfaz, a continuación, deserializar el uso sólo de la interfaz y una InstanceCreator?

Por defecto, parece que gson creará el hormigón de la instancia, pero no establece los campos de cultivo.

Problema se registra aquí:

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

0voto

paul Puntos 612

Usted tiene que usar TypeToken clase de Google. Será necesario, por supuesto, tiene una clase genérica T para hacer obras

Tipo de fooType = new TypeToken>() {}.getType(); gson.toJson(foo, fooType);

gson.fromJson(json, fooType);

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by: