2012-06-27 19 views
9

Sto cercando di utilizzare GSON con un'interfaccia:Deserializzare un'interfaccia 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); 

Dal momento che la classe wrapper ha una variabile membro che è di tipo foto, come posso fare per la deserializzazione utilizzando GSON?

Grazie

risposta

8

La deserializzazione personalizzata è necessaria.

seconda del problema più grande da risolvere, o un [ "adattatore tipo"] 1 o un "type hierarchy adapter" deve essere utilizzato. L'adattatore di gerarchia di tipi "is to cover the case when you want the same representation for all subtypes of a type".

+1

Ciao Bruce, quindi penso di essere coinvolto in quello che è, sembra che Gson non supporti ancora la deserializzazione polimorfica. Ho visto sul tuo sito che hai alcuni esempi di jackson (che includono il polimorfismo). Consiglieresti di usare jackson over gson a questo punto? Grazie – user291701

+2

Sì, mi consiglia di utilizzare Jackson su Gson, come delineato in http://programmerbruce.blogspot.com/2011/07/gson-v-jackson-part-6.html. Jackson ha un supporto integrato decente per il polimorfismo, oltre ad altri vantaggi, come prestazioni significativamente migliori. Per gli interessati, il post sulla deserializzazione polimorfica con Jackson è disponibile all'indirizzo http://programmerbruce.blogspot.com/2011/05/deserialize-json-with-jackson-into.html –

3

In poche parole, non è possibile farlo con GSON.

Sono stato turbato dallo stesso problema quando mi sono imbattuto su Jackson. Con esso è molto facile:

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

e poi si può andare a de/serializzazione gli oggetti Java e interfacce, senza dover scrivere aggiuntivi personalizzati de/Serializzatori, annotaions e davvero nessun codice aggiunto di sorta.

Questa non era una parte della domanda, ma potrebbe rivelarsi utile se si decide di effettuare il porting da Gson a Jackson. Gson supporta i campi privati ​​per impostazione predefinita, ma per Jackson devi includere questo nel tuo codice.

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

implementazione di esempio per il codice in principale:

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 promesso che lavoreranno su questo problema nelle versioni future, ma non hanno risolto finora. Se questo è molto importante per la tua applicazione ti suggerirei di portarti a Jackson.

+0

manca il link – ronalchn

0

Ho creato un generatore di shim di interfaccia primitivo compilando una classe di proprietà groovy per interoperare con un modello Autobeans GWT. questo è un metodo veramente approssimativo per aggirare la curva di apprendimento ASM/cglib per ora. background su questo: con Autobeans, puoi usare solo le interfacce, e il sole. * i proxy non sono in grado di interoperare con GSON per tutti i tentativi di accesso che ho sperimentato. MA, quando groove classloader è locale a GsonBuilder, le cose diventano un po 'più semplici. nota, questo fallisce a meno che la registrazione di gsonBuilder non sia effettivamente chiamata all'interno dello stesso groovy.

per accedere alla fabbrica di spessore creare uno come nomi Singleton JSON_SHIM e chiamare

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

di registrare, se necessario, e creare un [blank] istanza. se hai interfacce nelle tue interfacce, devi pre-registrarle troppo prima dell'uso. questa è solo una magia sufficiente per usare flat Autobeans con gson, non un intero framework. non c'è alcun codice groovy in questo generatore, quindi qualcuno con javassist-foo può ripetere l'esperimento.

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