9

我正在尝试将 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);

由于 Wrapper 类有一个 Photo 类型的成员变量,我该如何使用 Gson 对其进行反序列化?

谢谢

4

3 回答 3

8

自定义反序列化是必要的。

根据要解决的更大问题,应该使用["type adapter"] 1"type hierarchy adapter"。类型层次结构适配器“是为了涵盖当您想要一个类型的所有子类型具有相同表示的情况”

于 2012-06-29T18:39:11.220 回答
5

简而言之,你不能用 GSON 做到这一点。

当我偶然发现杰克逊时,我也被同样的问题困扰。有了它很容易:

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

然后你可以去反序列化你的 Java 对象和接口,而无需编写额外的自定义反序列化器、注释,并且实际上没有添加任何代码。

这不是问题的一部分,但如果您决定从 Gson 移植到 Jackson,可能会很有用。Gson 默认支持私有字段,但对于 Jackson,您必须将其包含在您的代码中。

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

您的代码在 main 中的示例实现:

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 承诺他们将在未来的版本中解决这个问题,但到目前为止他们还没有解决这个问题。如果这对您的应用程序非常重要,我建议您移植到杰克逊。

于 2012-08-06T10:50:20.700 回答
0

我通过编译一个 groovy 属性类来构建一个原始接口 shim 生成器,以与 GWT Autobeans 模型进行互操作。这是目前回避 ASM/cglib 学习曲线的一种非常粗略的方法。背景:使用 Autobeans,您只能使用接口,而 sun.* 代理对于我尝试过的所有访问尝试都无法使用 gson 互操作。但是,当 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;
  }
}
于 2014-02-05T04:55:55.500 回答