Java Gson:如何处理可能具有不同类型的字段?

Java Gson:如何处理可能具有不同类型的字段?,java,json,gson,deserialization,Java,Json,Gson,Deserialization,我正在尝试使用Gson反序列化响应。数据由可嵌套到任意深度的节点列表组成。json如下所示: { "type": "node", "children": [ { "id": "abc123", "name": "Name 1", "subdata": { "type": "node", "children": [

我正在尝试使用Gson反序列化响应。数据由可嵌套到任意深度的节点列表组成。json如下所示:

{
    "type": "node",
    "children": [
        {
            "id": "abc123",
            "name": "Name 1",
            "subdata": {
                "type": "node",
                "children": [
                    {
                        "id": "def456",
                        "name": "Name 2"
                    }
                ]
            }
        }
    ]
}
public class ExtraData {
    private String type;
    private List<String> children;
}
现在,在没有任何自定义类型适配器的情况下,我可以使用以下类实现这一点:

public class Data {
    private String type;
    private List<Node> nodes;
}

public class Node {
    private String id;
    private String name;
    private Data subdata;
}
这当然可以表示为如下Java类:

{
    "type": "node",
    "children": [
        {
            "id": "abc123",
            "name": "Name 1",
            "subdata": {
                "type": "node",
                "children": [
                    {
                        "id": "def456",
                        "name": "Name 2"
                    }
                ]
            }
        }
    ]
}
public class ExtraData {
    private String type;
    private List<String> children;
}
公共类外部数据{
私有字符串类型;
私人名单儿童;
}

但问题是:我如何处理反序列化,以便
子数据可以是
数据
外部数据

给定节点的子节点似乎总是JSON数组,因此使用它们可以做的第一件事是将子节点声明为
列表
隐藏实际类型。但是,您仍然有
type
属性/字段,可以很好地获取子对象的实际类型。最简单的方法可能只是添加另一个JSON反序列化器,以便对
数据
实例进行反序列化,这会带来一些性能开销(因为这些实例不是类型适配器),而且据我所知,
数据
类的字段上缺少
@SerializedName

如果您对更改DTO类型也很在行,请选择枚举而不是原始字符串,因为它们与枚举配合得非常好(特别是与智能IDE协作时):

枚举类型{
@SerializedName(“节点”)
节点,
@序列化名称(“额外”)
额外的
}
Data
类本身可能如下所示:

最终类数据{
私有最终类型;
private final List children;//这个应该是:
//*如果类型=额外,则为任一列表
//*或如果类型=节点,则列出
数据(最终类型、最终列表子项){
this.type=type;
这个。孩子=孩子;
}
类型getType(){
返回类型;
}
列表getChildren(){
返回儿童;
}
}
由于
额外的
类型的子项在您的问题中只是字符串,只需将节点DTO添加到类:

final类节点{
@序列化名称(“id”)
私有最终字符串id=null;
@序列化名称(“名称”)
私有最终字符串名称=null;
@SerializedName(“子数据”)
私有最终数据子数据=null;
字符串getId(){
返回id;
}
字符串getName(){
返回名称;
}
数据getSubdata(){
返回子数据;
}
}
现在,在反序列化
数据
类时,您可以确定子列表的实际类型,并根据节点类型将其反序列化为字符串列表或节点列表。请注意,下面的反序列化程序使用
java.lang.reflect.Type
实例,而不是
java.lang.Class
,因为后者由于java泛型类型擦除而较弱,并且对于任何列表参数化(字符串、节点等)都是
List.Class
。使用类型标记提供预期类型后,只需将JSON键/值对委托给指定目标类型的反序列化上下文,从而进行递归反序列化,这将适用于任意嵌套元素级别(但是,如果我没有弄错的话,GSON有一些内部堆栈限制,限制为32)

final类DataJsonDeserializer
实现JsonDeserializer{
私有静态最终JsonDeserializer dataJsonDeserializer=新dataJsonDeserializer();
私有静态final java.lang.reflect.Type nodeListType=新类型令牌(){
}.getType();
私有静态final java.lang.reflect.Type stringListType=new-TypeToken(){
}.getType();
私有数据JSONDESerializer(){
}
静态JsonDeserializer getDataJsonDeserializer(){
返回dataJsonDeserializer;
}
@凌驾
公共数据反序列化(最终JsonElement JsonElement、最终java.lang.reflect.Type、最终JsonDeserializationContext)
抛出JsonParseException{
最终JsonObject rootJsonObject=jsonElement.getAsJsonObject();
最终类型nodeType=context.deserialize(rootJsonObject.get(“Type”)、Type.class);
final JsonArray childrenJsonArray=rootJsonObject.get(“children”).getAsJsonArray();
最后儿童名单;
开关(节点类型){
案例节点:
children=context.deserialize(childrenJsonArray,nodeListType);
打破
额外个案:
children=context.deserialize(childrenJsonArray,stringListType);
打破
违约:
抛出新的断言错误(nodeType);
}
返回新数据(节点类型、子节点);
}
}
以及递归遍历子项的演示(注意增强的
for
语句,这些语句将每个项转换为下面的目标类型):


不久前,我对杰克逊做了类似的事情:非常感谢!这似乎正是我想要的。
NODE
    abc123 Name 1
>NODE
    def456 Name 2

NODE
    abc123 Name 1
>EXTRA
    ghi
    jkl
    mno