我想将树结构中的数据表示为java对象,然后我想将其转换为JSON对象。
借助stackoverflow条目:
Convert java arrayList of Parent/child relation into tree?
我有以下主要功能和"对" list包含一对:child和parent
ArrayList<Pair> list= new ArrayList<>();
list.add(new Pair("6", "4"));
list.add(new Pair("5", "4"));
list.add(new Pair("4", "3"));
list.add(new Pair("2", "3"));
list.add(new Pair("3", "null"));
Map<String, Node> o_map= new HashMap<>();
for (Pair l: list) {
Node parent = o_map.getOrDefault(l.getParentId(), new Node(l.getParentId()));
Node child = o_map.getOrDefault(l.getChildId(), new Node(l.getChildId()));
parent.children.add(child);
child.parent = parent;
o_map.put(parent.id, parent);
o_map.put(child.id, child);
}
Gson gs = new Gson();
System.out.println(gs.toJson(o_map));
}
但是这段代码会返回:
Exception in thread "main" java.lang.StackOverflowError
at java.io.StringWriter.write(StringWriter.java:112)
at com.google.gson.stream.JsonWriter.string(JsonWriter.java:576)
at com.google.gson.stream.JsonWriter.writeDeferredName(JsonWriter.java:402)
at com.google.gson.stream.JsonWriter.beginArray(JsonWriter.java:287)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:95)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:61)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:112)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:239)
at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:968)
错误。
我不明白为什么它会返回这样的错误。 可能是什么原因? 非常感谢你提前。
答案 0 :(得分:1)
您没有包含Node
类定义,但我猜它看起来像这样:
public class Node {
public final String id;
public Node parent;
public final ArrayList<Node> children = new ArrayList<>();
public Node(String id) {
this.id = id;
}
}
这是在内存中表示树数据结构的好方法(忽略一些不相关的样式问题,比如使用公共字段),但不可能序列化。为什么?因为任何具有非空Node
的{{1}}都具有周期性关系 - 子项包含对其父项的引用,而父项又包含对子项的引用,而子项又包含对父项的引用,反过来包含.....
来自user guide:
请注意,您无法使用循环引用序列化对象,因为这将导致无限递归。
我们可以通过这个更简单的例子触发相同的错误:
parent
那么我们如何解决这个问题呢?这取决于你想要的行为。一个简单的选择是阻止Gson尝试序列化Node root = new Node("A");
Node child = new Node("B");
root.children.add(child);
child.parent = root;
System.out.println(new Gson().toJson(root)); // passing in child would similarly fail
字段(我们不需要它,因为我们可以从parent
列表重建它)。要做到这一点,只需mark parent
as transient
,Gson就不会在结果中包含它。如果明确记录父关系更有帮助,您可以类似地children
children
字段。序列化transient
字段的好处是,您只需传入根节点,就可以遍历整个树。
另一种选择是序列化不同于children
的数据结构 - 您当前正在将每个节点ID映射到其Map<String, Node>
对象(传递上,它包含对每个其他节点的引用),这意味着即使你修复了周期性关系,你仍然会得到一些奇怪的JSON。 似乎就像您真正想要的那样只是序列化ID - &gt;父母或身份证 - &gt;子关系,这将是一个Node
或Map<String, String>
数据结构,Gson可以顺利排序。如果这是您想要的结构,您可以先遍历树并构建这样的数据结构,或者定义custom deserializer,将Map<String, List<String>>
转换为您想要的精确JSON结构。