2012-06-27 15 views
9

Estoy tratando de usar con una interfaz Gson:Deserializar una interfaz usando Gson?

public interface Photo { 
    public int getWidth(); 
} 

public class DinosaurPhoto implements Photo { 
    ... 
} 

public class Wrapper { 
    private Photo mPhoto; // <- problematic 
} 

... 

Wrapper wrapper = new Wrapper(); 
wrapper.setPhoto(new DinosaurPhoto()); 
Gson gson = new Gson(); 
String raw = gson.toJson(wrapper); 

// Throws an error since "Photo" can't be deserialized as expected. 
Wrapper deserialized = gson.fromJson(raw, Wrapper.class); 

Desde la clase contenedora tiene una variable miembro que es del tipo de fotos, ¿cómo hago para deserializar usando Gson?

Gracias

Respuesta

8

La deserialización personalizada es necesaria.

Dependiendo del problema más grande que hay que resolver, ya sea un [ "adaptador de tipo"] 1 o una "type hierarchy adapter" debe ser utilizado. El tipo de jerarquía adaptador "is to cover the case when you want the same representation for all subtypes of a type".

+1

Hola Bruce, así que creo que estoy atrapado en lo que es, parece que gson simplemente no admite la deserialización polimórfica todavía. He visto en su sitio que tiene algunos ejemplos de Jackson (que incluyen polimorfismo). ¿Recomendarías usar jackson over gson en este punto? Gracias – user291701

+2

Sí, recomiendo usar Jackson sobre Gson, como se describe en http://programmerbruce.blogspot.com/2011/07/gson-v-jackson-part-6.html. Jackson tiene un soporte incorporado decente para el polimorfismo, así como otras ventajas, como un rendimiento significativamente mejor. Para los interesados, la publicación sobre la deserialización polimórfica con Jackson se encuentra en http://programmerbruce.blogspot.com/2011/05/deserialize-json-with-jackson-into.html –

3

En pocas palabras, no se puede hacer eso con GSON.

Estaba preocupado por el mismo problema cuando me encontré con Jackson. Con él es muy fácil:

ObjectMapper mapper = new ObjectMapper(); 
mapper.enableDefaultTyping(); 

y entonces usted puede ir sobre de/serialización de los objetos e interfaces Java sin tener que escribir adicionales personalizados de serializadores/annotaions, y realmente no hay código añadido alguno.

Esto no fue parte de la pregunta, pero puede resultar útil si decide realizar un porteo de Gson a Jackson. Gson admite campos privados de manera predeterminada, pero para Jackson debe incluir esto en su código.

mapper.setVisibilityChecker(g.getVisibilityChecker().with(Visibility.ANY)); 

aplicación de la muestra para su código en el principal:

ObjectMapper mapper = new ObjectMapper(); 
mapper.enableDefaultTyping(); 
mapper.setVisibilityChecker(g.getVisibilityChecker().with(Visibility.ANY)); 
Wrapper wrapper = new Wrapper(); 
wrapper.setPhoto(new DinosaurPhoto()); 
String wrapper_json = mapper.writeValueAsString(wrapper); 
Wrapper wrapper_from_json = mapper.readValue(wrapper_json,Wrapper.class); 

Gson prometió que van a trabajar en este problema en futuras versiones, pero no han resuelto hasta ahora. Si esto es muy importante para su aplicación, le sugiero que lo envíe a Jackson.

+0

. Falta su enlace – ronalchn

0

He creado un generador de shim de interfaz primitiva mediante la compilación de una clase de propiedades groovy para interoperar con un modelo GWT Autobeans. este es un método realmente rudo para eludir la curva de aprendizaje ASM/cglib por el momento. sobre esto: con Autobeans, solo puede usar interfaces, y los proxies sun * son incapaces de interferencia gson para todos los intentos de acceso con los que he experimentado. PERO, cuando el cargador de clases groovy es local para GsonBuilder, las cosas se vuelven un poco más fáciles. tenga en cuenta que esto falla, a menos que el registro de gsonBuilder sea realmente llamado dentro del propio groovy.

para acceder a la fábrica de cuña crear una como nombres únicos JSON_SHIM y llamar

JSON_SHIM.getShim ("{}", MyInterface.class)

para registrar si es necesario y crear una instancia [en blanco] si tiene interfaces en sus interfaces, debe registrarlas previamente antes de su uso. esto es suficiente magia para usar flat Autobeans con gson, no un framework completo. no hay un código groovy en este generador, por lo que alguien con javassist-foo puede repetir el experimento.

import com.google.gson.GsonBuilder; 
import com.google.gson.InstanceCreator; 
import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory; 
import groovy.lang.GroovyClassLoader; 
import org.apache.commons.beanutils.PropertyUtils; 

import java.beans.PropertyDescriptor; 
import java.lang.reflect.InvocationTargetException; 
import java.lang.reflect.Method; 
import java.lang.reflect.Type; 
import java.util.LinkedHashMap; 
import java.util.Map; 

public class GroovyGsonShimFactory { 
    private Map<Class, Method> shimMethods = new LinkedHashMap<>(); 

    private void generateGroovyProxy(Class ifaceClass) { 
    String shimClassName = ifaceClass.getSimpleName() + "$Proxy"; 
    String ifaceClassCanonicalName = ifaceClass.getCanonicalName(); 
    String s = "import com.google.gson.*;\n" + 
     "import org.apache.commons.beanutils.BeanUtils;\n" + 
     "import java.lang.reflect.*;\n" + 
     "import java.util.*;\n\n" + 
     "public class "+shimClassName+" implements "+ifaceClassCanonicalName+" {\n" ; 

    { 
     PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(ifaceClass); 
     for (PropertyDescriptor p : propertyDescriptors) { 
     String name = p.getName(); 
     String tname = p.getPropertyType().getCanonicalName(); 
     s += "public " + tname + " " + name + ";\n"; 
     s += " " + p.getReadMethod().toGenericString().replace("abstract", "").replace(ifaceClassCanonicalName + ".", "") + "{return " + name + ";};\n"; 
     Method writeMethod = p.getWriteMethod(); 
     if (writeMethod != null) 
      s += " " + writeMethod.toGenericString().replace("abstract", "").replace(ifaceClassCanonicalName + ".", "").replace(")", " v){" + name + "=v;};") + "\n\n"; 
     } 
    } 
    s+=  " public static "+ifaceClassCanonicalName+" fromJson(String s) {\n" + 
     " return (" +ifaceClassCanonicalName+ 
     ")cydesign.strombolian.server.ddl.DefaultDriver.gson().fromJson(s, "+shimClassName+".class);\n" + 
     " }\n" + 
     " static public interface foo extends InstanceCreator<"+ifaceClassCanonicalName+">, JsonSerializer<"+ifaceClassCanonicalName+">, JsonDeserializer<"+ifaceClassCanonicalName+"> {}\n" + 
     " static {\n" + 
     " cydesign.strombolian.server.ddl.DefaultDriver.builder().registerTypeAdapter("+ifaceClassCanonicalName+".class, new foo() {\n" + 
     "  public "+ifaceClassCanonicalName+" deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {\n" + 
     "  return context.deserialize(json, "+shimClassName+".class);\n" + 
     "  }\n" + 
     "\n" + 
     "  public "+ifaceClassCanonicalName+" createInstance(java.lang.reflect.Type type) {\n" + 
     "  try {\n" + 
     "   return new "+shimClassName+"();\n" + 
     "  } catch (Exception e) {\n" + 
     "   e.printStackTrace(); \n" + 
     "  }\n" + 
     "  return null;\n" + 
     "  }\n" + 
     "\n" + 
     "  @Override\n" + 
     "  public JsonElement serialize("+ifaceClassCanonicalName+" src, Type typeOfSrc, JsonSerializationContext context) {\n" + 
     "  LinkedHashMap linkedHashMap = new LinkedHashMap();\n" + 
     "  try {\n" + 
     "   BeanUtils.populate(src, linkedHashMap);\n" + 
     "   return context.serialize(linkedHashMap);\n" + 
     "  } catch (Exception e) {\n" + 
     "   e.printStackTrace(); \n" + 
     "  }\n" + 
     "\n" + 
     "  return null;\n" + 
     "  }\n" + 
     " });\n" + 
     " }\n\n" + 
     "};"; 

    System.err.println("" + s); 
    ClassLoader parent = DefaultDriver.class.getClassLoader(); 
    GroovyClassLoader loader = new GroovyClassLoader(parent); 

    final Class gClass = loader.parseClass(s); 
    try { 
     Method shimMethod = gClass.getMethod("fromJson", String.class); 
     shimMethods.put(ifaceClass, shimMethod); 
    } catch (NoSuchMethodException e) { 
     e.printStackTrace(); 
    } 

    } 

    public <T> T getShim(String json, Class<T> ifaceClass) { 
    if (!shimMethods.containsKey(ifaceClass)) 
     generateGroovyProxy(ifaceClass); 
    T shim = null;//= gson().shimMethods(json, CowSchema.class); 
    try { 
     shim = (T) shimMethods.get(ifaceClass).invoke(null, json); 
    } catch (IllegalAccessException | InvocationTargetException e) { 
     e.printStackTrace(); 
    } 
    return shim; 
    } 
} 
Cuestiones relacionadas