给定节点的子节点似乎总是 JSON 数组,因此您可以对它们做的第一件事是将子节点声明为 List<?> 隐藏实际类型。但是,您仍然拥有 type 属性/字段,它非常适合获取子项的实际类型。最简单的方法可能只是添加另一个 JSON 反序列化器,以便以一些性能成本反序列化 Data 实例(因为这些不是类型适配器),并且据我所知,@SerializedName 在 @987654325 的字段中缺少@类。
如果您也可以更改 DTO 类型,请更喜欢枚举而不是原始字符串,因为它们与枚举完美搭配(尤其是与智能 IDE 合作时):
enum Type {
@SerializedName("node")
NODE,
@SerializedName("extra")
EXTRA
}
Data 类本身可能如下所示:
final class Data {
private final Type type;
private final List<?> children; // this one is supposed to be:
// * either List<String> if type=EXTRA
// * or List<Node> if type=NODE
Data(final Type type, final List<?> children) {
this.type = type;
this.children = children;
}
Type getType() {
return type;
}
List<?> getChildren() {
return children;
}
}
由于extra-typed 子项只是您问题中的字符串,因此只需添加节点 DTO 类:
final class Node {
@SerializedName("id")
private final String id = null;
@SerializedName("name")
private final String name = null;
@SerializedName("subdata")
private final Data subdata = null;
String getId() {
return id;
}
String getName() {
return name;
}
Data getSubdata() {
return subdata;
}
}
现在,在反序列化 Data 类时,您可以确定子列表的实际类型,并根据节点类型将其反序列化为字符串列表或节点列表。请注意,下面的反序列化器使用java.lang.reflect.Type 实例而不是java.lang.Class,因为后者由于Java 泛型类型擦除而很弱,并且对于任何列表参数化(字符串、节点等)都是List.class。使用类型标记提供预期类型,只需将 JSON 键/值对委托给指定目标类型的反序列化上下文,从而使递归反序列化适用于任意嵌套元素级别(但是,GSON 有一些内部堆栈限制,限制为 32如果我没记错的话)。
final class DataJsonDeserializer
implements JsonDeserializer<Data> {
private static final JsonDeserializer<Data> dataJsonDeserializer = new DataJsonDeserializer();
private static final java.lang.reflect.Type nodeListType = new TypeToken<List<Node>>() {
}.getType();
private static final java.lang.reflect.Type stringListType = new TypeToken<List<String>>() {
}.getType();
private DataJsonDeserializer() {
}
static JsonDeserializer<Data> getDataJsonDeserializer() {
return dataJsonDeserializer;
}
@Override
public Data deserialize(final JsonElement jsonElement, final java.lang.reflect.Type type, final JsonDeserializationContext context)
throws JsonParseException {
final JsonObject rootJsonObject = jsonElement.getAsJsonObject();
final Type nodeType = context.deserialize(rootJsonObject.get("type"), Type.class);
final JsonArray childrenJsonArray = rootJsonObject.get("children").getAsJsonArray();
final List<?> children;
switch ( nodeType ) {
case NODE:
children = context.deserialize(childrenJsonArray, nodeListType);
break;
case EXTRA:
children = context.deserialize(childrenJsonArray, stringListType);
break;
default:
throw new AssertionError(nodeType);
}
return new Data(nodeType, children);
}
}
以及递归遍历子项的演示(注意增强的for 语句将每个项目转换为下面的目标类型):
public final class EntryPoint {
private static final String JSON_WITH_SUBNODES = "{\"type\":\"node\",\"children\":[{\"id\":\"abc123\",\"name\":\"Name 1\",\"subdata\":{\"type\":\"node\",\"children\":[{\"id\":\"def456\",\"name\":\"Name 2\"}]}}]}";
private static final String JSON_WITH_REFERENCES = "{\"type\":\"node\",\"children\":[{\"id\":\"abc123\",\"name\":\"Name 1\",\"subdata\":{\"type\":\"extra\",\"children\":[\"ghi\",\"jkl\",\"mno\"]}}]}";
private static final Gson gson = new GsonBuilder()
.registerTypeAdapter(Data.class, getDataJsonDeserializer())
.create();
public static void main(final String... args) {
process(gson.fromJson(JSON_WITH_SUBNODES, Data.class));
process(gson.fromJson(JSON_WITH_REFERENCES, Data.class));
}
private static void process(final Data data) {
process(data, 0);
out.println();
}
private static void process(final Data data, final int level) {
for ( int i = 0; i < level; i++ ) {
out.print('>');
}
final List<?> children = data.getChildren();
final Type type = data.getType();
out.println(type);
switch ( type ) {
case NODE:
@SuppressWarnings("unchecked")
final Iterable<Node> nodeChildren = (Iterable<Node>) children;
for ( final Node node : nodeChildren ) {
out.printf("\t%s %s\n", node.getId(), node.getName());
final Data subdata = node.getSubdata();
if ( subdata != null ) {
process(subdata, level + 1);
}
}
break;
case EXTRA:
@SuppressWarnings("unchecked")
final Iterable<String> extraChildren = (Iterable<String>) children;
for ( final String extra : extraChildren ) {
out.printf("\t%s\n", extra);
}
break;
default:
throw new AssertionError(type);
}
}
}
输出:
NODE
abc123 Name 1
>NODE
def456 Name 2
NODE
abc123 Name 1
>EXTRA
ghi
jkl
mno