更新时间:2023-01-17 15:56:25
您没有包含 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;
$ b这是一种很好的方式来表示树形数据结构在内存中 Node
父$ c>具有循环关系 - 子项包含对其父项的引用,反过来又包含对孩子的引用,而孩子又包含对父代的引用,而该引用又包含.....
从用户指南:
请注意,您不能使用循环引用序列化对象,因为这会导致无限递归。
我们可以用这个简单的例子触发相同的错误:
节点root =新节点(A);
节点child =新节点(B);
root.children.add(child);
child.parent = root;
System.out.println(new Gson()。toJson(root)); //传入孩子同样会失败那么我们该如何解决这个问题呢?这取决于你想要的行为。一个简单的选择是阻止Gson试图序列化
父
字段(我们不需要它,因为我们可以从children
列表)。只需将父母
标记为transient
,Gson不会将其包含在结果中。如果显式记录父亲关系更有帮助,您可以同样使children
transient
字段。然而,序列化children
字段的好处是你可以传入根节点并遍历整个树。另一种选择是序列化不同于
Map< String,Node>
的数据结构 - 您当前将每个节点ID映射到它的Node
对象(它在传递中包含对每个其他节点的引用),这意味着即使您修复了循环关系,您仍然会得到一些奇怪的JSON。它似乎像你想要的只是序列化ID - >父或ID - >子关系,它将是一个Map< String,String>
或映射<字符串,列表< String>>
数据结构,Gson在序列化时没有问题。如果这是你想要的结构,你可以简单地遍历你的树并首先构造这样一个数据结构,或者定义一个自定义反序列化器,它将Node
转换为您想要的确切JSON结构。I want to represent data in tree structure as java object then I want to convert it to JSON object.
With the help of *** entries:
Convert java arrayList of Parent/child relation into tree?
I had below main function and "pairs" list contains a pair: child and 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)); }
However this code returns:
Exception in thread "main" java.lang.***Error 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)
error.
I don't get why it returns such an error. What could be the reason? Thank you very much in advance.
You didn't include your
Node
class definition, but I'm guessing it looks something like this:public class Node { public final String id; public Node parent; public final ArrayList<Node> children = new ArrayList<>(); public Node(String id) { this.id = id; } }
This is a fine way to represent a tree data structure in memory (ignoring some unrelated style issues like using public fields), but it's impossible to serialize. Why? Because any
Node
with a non-nullparent
has a cyclical relationship - a child contains a reference to its parent, which in turn contains a reference to the child, which in turn contains a reference to the parent, which in turn contains .....From the user guide:
Note that you can not serialize objects with circular references since that will result in infinite recursion.
We can trigger the same error with this simpler example:
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
So how can we fix this? It depends on what behavior you want. One easy option is to prevent Gson from attempting to serialize the
parent
field (we don't need it, as we can reconstruct it from thechildren
list). To do this just markparent
astransient
and Gson will not include it in the result. You could similarly makechildren
thetransient
field if explicitly recording the parent relationship is more helpful. A benefit of serializing thechildren
field however is that you can just pass in the root node and the whole tree will be traversed.Another option is to serialize a different data structure than
Map<String, Node>
- you're currently mapping each node ID to itsNode
object (which, transitively, contains a reference to every other node), meaning that even if you fix the cyclical relationship you're still going to get some odd JSON as a result. It seems like what you'd really want is to just serialize the ID -> parent or ID -> children relationships, which would be aMap<String, String>
orMap<String, List<String>>
data structure which Gson will have no trouble serializing. If that's the structure you want you could simply traverse your tree and construct such a data structure first, or define a custom deserializer which converts aNode
into the exact JSON structure you want.