【问题标题】:Deserializing an interface using Gson?使用 Gson 反序列化接口?
【发布时间】:2012-06-27 22:51:10
【问题描述】:

我正在尝试将 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 对其进行反序列化?

谢谢

【问题讨论】:

    标签: gson


    【解决方案1】:

    需要自定义反序列化。

    根据要解决的较大问题,应使用 ["type adapter"] 1 或 "type hierarchy adapter"。类型层次适配器"is to cover the case when you want the same representation for all subtypes of a type"。

    【讨论】:

    • 嗨布鲁斯,所以我想我已经了解了什么,看起来 gson 还不支持多态反序列化。我在您的网站上看到您有一些杰克逊示例(包括多态性)。你会推荐在这一点上使用杰克逊而不是 gson 吗?谢谢
    • 是的,我确实建议使用 Jackson 而不是 Gson,如 programmerbruce.blogspot.com/2011/07/gson-v-jackson-part-6.html 中所述。 Jackson 对多态性具有良好的内置支持,以及其他优势,例如显着更好的性能。对于感兴趣的人,关于 Jackson 的多态反序列化的帖子位于 programmerbruce.blogspot.com/2011/05/…
    【解决方案2】:

    简而言之,GSON 无法做到这一点。

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

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

    【讨论】:

      【解决方案3】:

      我通过编译一个 groovy 属性类来构建一个原始接口 shim 生成器,以与 GWT Autobeans 模型进行互操作。这是目前回避 ASM/cglib 学习曲线的一种非常粗略的方法。背景:使用 Autobeans,您只能使用接口,而 sun.* 代理对于我尝试过的所有访问尝试都无法使用 gson 互操作。但是,当 groovy 类加载器是 GsonBuilder 的本地时,事情会变得容易一些。请注意,除非 gsonBuilder 注册实际上是从 groovy 本身调用的,否则这将失败。

      要访问 shim 工厂,请创建一个作为单例名称 JSON_SHIM 并调用

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

      在需要时注册并创建一个 [空白] 实例。如果您的接口中有接口,则必须在使用前预先注册这些接口。这足以将 flat 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;
        }
      }
      

      【讨论】:

        猜你喜欢
        • 2011-12-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-04
        • 2017-01-27
        相关资源
        最近更新 更多