java - 使用 GSON 反序列化的父对象的参数实例化子对象并使用泛型?

标签 java android generics gson

我大致有以下结构

class MyDeserialParent<T extends MyChildInterface> {

     MyChildInterface mSerialChild;
     ... //some other fields (not 'type')

}

但是它是从一个杂乱的 JSON 结构反序列化的,子节点的两个属性在父节点上返回,如下所示。

{
    "myDeserialParents" : [
        {
            ... //some parent properties
            "type": "value", //used in a TypeAdapter to choose child implementation
            "childProp1": "1",
            "childProp2": "2",
         },
         ... //more in this list
     ]
}

显然,这会阻止我仅使用 SerializedName 注释 mSerialChild 并让 TypeAdapter 发挥其魔力。所以我希望做的是,当 MyDeserialParent 被反序列化时,使用“类型”找到 MyChildInterface 的正确具体类,并使用 childProp1< 创建一个新类childProp2 作为构造函数的参数。我不知道该怎么做。

我可以想象为 MyDeserialParent 使用 TypeAdapter (JsonDeserializer) 并在 deserialize 中获取类型字段 (以及两个子属性),然后自己为 MyChildInterface 实例化正确的具体内容。

这意味着我必须创建我的 MyDeserialParent 类(使用 context.deserialize(json, MyDeserialParent.class))并使用 MyChildInterface< 调用 setter/ 实例。感觉不对劲,就像我错过了什么。有没有更好的办法?

如果我也手动创建父对象,是否还有一种方法可以指定泛型(T on MyDeserialParent)?还是类型删除意味着没有办法做到这一点? (这个问题不太重要,因为我知道如果我使用已经推断出 T 的 MyDeserialParent 的特定子类型,我可以获得类型安全,但我想避免它)

最佳答案

您显然需要自定义 TypeAdapter。但棘手的部分是:

  • 你的父类是一个通用类
  • mSerialChild 不是T 类型,而是MyChildInterface
  • 类型
  • 我们希望避免为每个子类手动解析 json,并且能够向父类添加属性而无需修改整个代码。

牢记这一点,我最终得出了以下解决方案。

public class MyParentAdapter implements JsonDeserializer<MyDeserialParent>{

    private static Gson gson = new GsonBuilder().create();
    // here is the trick: keep a map between "type" and the typetoken of the actual child class
    private static final Map<String, Type> CHILDREN_TO_TYPETOKEN;

    static{
        // initialize the mapping once
        CHILDREN_TO_TYPETOKEN = new TreeMap<>();
        CHILDREN_TO_TYPETOKEN.put( "value", new TypeToken<MyChild1>(){}.getType() );
    }


    @Override
    public MyDeserialParent deserialize( JsonElement json, Type t, JsonDeserializationContext
            jsonDeserializationContext ) throws JsonParseException{
        try{
            // first, get the parent
            MyDeserialParent parent = gson.fromJson( json, MyDeserialParent.class );
            // get the child using the type parameter
            String type = ((JsonObject)json).get( "type" ).getAsString();
            parent.mSerialChild = gson.fromJson( json, CHILDREN_TO_TYPETOKEN.get( type ) );
            return parent;

        }catch( Exception e ){
            e.printStackTrace();
        }
        return null;
    }
}

备注:

  • 自定义适配器必须在 gsonBuilder 上注册
  • 如果您需要为您的 child 定制一些 gson 属性,您可以在 MyParentAdapter 的构造函数中传递 Gson 对象,因为现在它使用默认值;
  • child 和 parent 必须具有不同名称的属性
  • 每个新类型都必须添加到具有相应类的 map 中。

完整示例

主要:

public class DeserializeExample{

    MyDeserialParent[] myDeserialParents;

    static String json = "{\n" +
            "    \"myDeserialParents\" : [\n" +
            "        {\n" +
            "            \"otherProp\": \"lala\"," +
            "            \"type\": \"value\", //used in a TypeAdapter to choose child implementation\n" +
            "            \"childProp1\": \"1\",\n" +
            "            \"childProp2\": \"2\"\n" +
            "         }\n" +
            "     ]\n" +
            "}";


    public static void main( String[] args ){
        Gson gson = new GsonBuilder().registerTypeAdapter( MyDeserialParent.class, new MyParentAdapter() ).create();
        DeserializeExample result = gson.fromJson( json, DeserializeExample.class );
        System.out.println( gson.toJson( result ));
        // output: 
        // {"myDeserialParents":[{"mSerialChild":{"childProp1":"1","childProp2":"2"},"otherProp":"lala"}]}
    }//end main

}//end class

家长:

class MyDeserialParent<T extends MyChildInterface>{

    MyChildInterface mSerialChild;
    //some other fields (not 'type')
    String otherProp;
}

child :

public class MyChild1 implements MyChildInterface {
    String childProp1;
    String childProp2;
}//end class

关于java - 使用 GSON 反序列化的父对象的参数实例化子对象并使用泛型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35295374/

相关文章:

swift - 如何在 Swift 中创建接受 Double 和 Int 的泛型类

java - 如何使用 HttpURLConnection 通过登录下载文件

java - 使用java aspose库将html转换为ppt

android - 关于 DDMS 方法分析器的问题

Android ListView CHOICE_MODE_MULTIPLE,如何设置检查索引?

java - List、List<?>、List<T>、List<E> 和 List<Object> 的区别

java - Java 线程(大学练习)

java - 如何创建一个可以作为窗口服务运行的java应用程序?

安卓 Activity 组

c# - 无法理解 "Generics"示例