java – 使用GSON反序列化的父对象的params实例化子对象,并使用泛型?

我有大致如下的结构

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),并在反序列化中获取类型字段(以及两个子属性),然后自己实例化MyChildInterface的正确具体.

这意味着我必须创建MyDeserialParent类(使用context.deserialize(json,MyDeserialParent.class)),并使用MyChildInterface实例调用setter.觉得我错过了一些错误.有没有更好的办法?

如果我还手动创建父对象,还有一种方法来指定泛型(T on MyDeserialParent)吗?或者类型Erasure是否意味着没有办法这样做? (这个问题不太重要,因为我知道如果我使用已经推测出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上注册
>如果您的孩子需要一些自定义gson属性,则可以在MyParentAdapter的构造函数中传递Gson对象,因为现在它使用默认属性,
儿童和父母必须具有不同名称的属性;
>每个新类型都必须使用相应的类添加到地图中.

完整的例子

主要:

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

儿童:

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

转载注明原文:java – 使用GSON反序列化的父对象的params实例化子对象,并使用泛型? - 代码日志