2012-06-27 40 views
9

我試圖使用GSON與接口:使用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); 

因爲包裝類有一個成員變量的類型是照片,我怎麼去使用GSON反序列化呢?

感謝

回答

8

定製反序列化是必要的。

取決於較大要解決的問題,無論是 [「型適配器」] 1"type hierarchy adapter"應該被使用。類型層次適配器"is to cover the case when you want the same representation for all subtypes of a type"

+1

嗨布魯斯,所以我想我抓到了什麼是什麼,看起來像GSON只是不支持多態反序列化。我在你的網站上看到你有一些傑克遜的例子(包括多態性)。你會推薦在這個時候使用傑克遜而不是gson嗎?謝謝 – user291701

+2

是的,我建議在Gson上使用Jackson,如http://programmerbruce.blogspot.com/2011/07/gson-v-jackson-part-6.html中所述。傑克遜擁有體面的內置支持多態性,以及其他優勢,如顯着更好的性能。對於感興趣的,關於Jackson的多態反序列化的帖子在http://programmerbruce.blogspot.com/2011/05/deserialize-json-with-jackson-into.html –

3

簡而言之,你不能用GSON來做到這一點。

當我偶然發現Jackson時,我被同樣的問題困擾。 有了它,它很容易:

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

然後你可以去德/序列化Java對象和接口,而無需編寫額外的自定義德/串行化器,annotaions確實沒有添加任何代碼。

這不是問題的一部分,但如果您決定從Gson移植到Jackson,可能會證明是有用的。 Gson默認支持私有字段,但對於Jackson,您必須在代碼中包含此字段。在主

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

示例實施代碼:

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承諾,他們將在未來的版本中這個問題的工作,但他們至今沒有解決它。 如果這對你的應用程序非常重要,我建議你移植到傑克遜。

+0

你的鏈接缺失 – ronalchn

0

我已經通過編譯一個groovy屬性類來與GWT Autobeans模型進行互操作,構建了一個原始接口Shim生成器。這是目前避開ASM/cglib學習曲線的非常粗略的方法。背景:對於Autobeans,您只能使用接口,並且對於我嘗試過的所有訪問嘗試,sun。*代理不能使用gson interop。但是,當Groovy類加載器是GsonBuilder本地時,事情變得更容易一些。請注意,除非gsonBuilder註冊實際上是從groovy本身調用的,否則這將失敗。

訪問墊片工廠創建一個作爲一個單名JSON_SHIM和呼叫

JSON_SHIM.getShim( 「{}」,MyInterface.class)

如果需要註冊並創建一個[空白]實例。如果您的界面中有界面,您必須預先註冊這些界面,以免超出使用範圍。這只是足夠的神奇使用平板 Autobeans與GSON,而不是一個完整的框架。 這個生成器中沒有groovy代碼,所以有javassist-foo的人可以重複這個實驗。

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