給定節點的孩子總是看起來像是JSON數組,所以你可以用它們做的第一件事就是將子女聲明爲隱藏實際類型的List<?>
。但是,您仍然擁有type
屬性/字段,這對於獲取子類的實際類型來說非常合適。最簡單的方法可能就是添加另一個JSON解串器,以反序列化具有一些性能成本的Data
實例(因爲它們不是類型適配器),並且據我所知,Data
類的字段上缺少@SerializedName
。
如果你也沒事改變你的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
我前段時間和傑克遜做了類似的事情:http://stackoverflow.com/a/12459070/823393 – OldCurmudgeon