我大致有以下结构
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/