将HashMap转换为JSON对象时,使用Gson时遇到了stackoverflowerror的问题

3
我希望将树形结构的数据表示为Java对象,然后将其转换为JSON对象。
借助stackoverflow条目: 将Java ArrayList转换为树? 使用GSON将HashMap转换为JSON 下面是我的主要函数,"pairs"列表包含一对子项和父项。
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)

错误。

我不明白为什么会返回这样的错误。可能是什么原因呢? 非常感谢您提前的帮助。


我需要做的是将一个hashmap<String, List<String>>转换为json对象。 - nenana
你应该这样写:ArrayList<Pair> list = new ArrayList<Pair>(); - Mark
我需要在JSON中拥有树形结构。为此,我需要将哈希映射转换为JSON。我尝试了另一种替代方法,将一对列表写入JSON,但结果并不是我所期望的。 - nenana
1
“结果不是我所期望的”-分享你期望的结果会很有帮助。同时,包括“Node”的定义也会很有帮助,因为这是您正在尝试序列化的内容。 - dimo414
1个回答

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;
  }
}

这是一种很好的表示树形数据结构在内存中的方式(忽略一些不相关的样式问题,比如使用公共字段),但它无法进行序列化。为什么?因为任何具有非空父级的节点都具有循环关系——子节点包含对其父节点的引用,而父节点反过来又包含对子节点的引用,依此类推,导致无限递归。

user guide中可以看到:

请注意,您不能序列化具有循环引用的对象,因为那会导致无限递归。

我们可以通过这个更简单的例子触发相同的错误:

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

那么我们该如何解决这个问题呢?这取决于您想要的行为。一个简单的选择是防止Gson尝试序列化parent字段(我们不需要它,因为我们可以从children列表重建它)。只需将 parent标记为transient,Gson就不会将其包含在结果中。如果明确记录父关系更有帮助,则同样可以使children成为transient字段。然而,序列化children字段的好处是,您只需传入根节点,整个树就会被遍历。

另一个选择是序列化与Map<String,Node>不同的数据结构 - 您当前正在将每个节点ID映射到其Node对象(传递地包含对每个其他节点的引用),这意味着即使您修复了循环关系,结果仍然会得到一些奇怪的JSON。看起来你真正想要的是只序列化ID -> 父级或ID -> 子级关系,这将是一个Map<String,String>Map<String,List<String>>数据结构,Gson可以轻松序列化。如果这是您想要的结构,您可以简单地遍历树并首先构建这样的数据结构,或者定义一个自定义反序列化程序,将Node转换为您想要的确切JSON结构。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接