3

I want to represent data in tree structure as java object then I want to convert it to JSON object.

With the help of stackoverflow entries:

Convert java arrayList of Parent/child relation into tree?

hashmap to JSON using GSON

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.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)

error.

I don't get why it returns such an error. What could be the reason? Thank you very much in advance.

Community
  • 1
  • 1
nenana
  • 57
  • 1
  • 10
  • What I need to do is to convert a hashmap> to json object. – nenana Oct 17 '16 at 06:22
  • shouldnt you write it like this `ArrayList list= new ArrayList();` – Mark Oct 17 '16 at 06:57
  • I need to have tree structure in json. To do that I need to have hashmap converted to json. I tried the other alternative, writing a pair list to json, but outcome wasn't what I look for. – nenana Oct 17 '16 at 07:10
  • 1
    "*outcome wasn't what I look for*" - it's helpful to share what outcome you *are* looking for. Including the definition of `Node` would also be helpful, since that's what you're trying to serialize. – dimo414 Nov 14 '16 at 05:57

1 Answers1

1

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-null parent 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 the children list). To do this just mark parent as transient and Gson will not include it in the result. You could similarly make children the transient field if explicitly recording the parent relationship is more helpful. A benefit of serializing the children 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 its Node 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 a Map<String, String> or Map<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 a Node into the exact JSON structure you want.

Community
  • 1
  • 1
dimo414
  • 47,227
  • 18
  • 148
  • 244