在将hashmap转换为JSON对象时从Gson获取stackoverflower

时间:2016-10-17 06:10:31

标签: json hashmap gson

我想将树结构中的数据表示为java对象,然后我想将其转换为JSON对象。

借助stackoverflow条目:

Convert java arrayList of Parent/child relation into tree?

hashmap to JSON using GSON

我有以下主要功能和"对" 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)

错误。

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

1 个答案:

答案 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;子关系,这将是一个NodeMap<String, String>数据结构,Gson可以顺利排序。如果这是您想要的结构,您可以先遍历树并构建这样的数据结构,或者定义custom deserializer,将Map<String, List<String>>转换为您想要的精确JSON结构。