1

I am trying to serialize a tree with GSon. This is the class of my TreeNode I want to serialize:

public class TreeNode {
private TreeNode parent;
private ArrayList<TreeNode> children;
private Object value;
    //methods
}

And my GSon calls look like this:

    TreeNode headNode = getHeadNode();
    Gson gson = new Gson();
    Type typeOfSrc = new TypeToken<TreeNode>(){}.getType();
    String gsonTreeString = gson.toJson(headNode,typeOfSrc);

As soon the headNode has at least one child, a stack overflow occurs and I don't understand why. Can someone tell me what I am doing wrong?

makes
  • 6,438
  • 3
  • 40
  • 58
user867204
  • 265
  • 4
  • 16

3 Answers3

2

In Gson, you can create a custom adapter to serialize a data structure that represents a tree. What you want is to serialize the node and all its children. The trick to do so is to call the write method recursively to fill in the JsonWriter. The following code should work for your example:

public class TreeNodeAdapter extends TypeAdapter<TreeNode> {

    @Override
    public void write(JsonWriter jsonWriter, TreeNode node) throws IOException {
        jsonWriter.beginObject()
                .name("coordinates")
                .jsonValue("\"" + node.getValue + "\"")
                .name("children")
                .beginArray();
        // Recursive call to the children nodes
        for (Node c : node.getChildren()) {
            this.write(jsonWriter, c);
        }
        jsonWriter.endArray()
                .endObject();
    }

    @Override
    public Node read(JsonReader jsonReader) throws IOException {
        return null;
    }
}

Then, to use the TreeNodeAdapter just instatiate NodeTree and use a GsonBuilder with your custom adapter to serialize your object.

 NodeTree tree = new NodeTree(...);
        NodeAdapter nodeAdapter = new NodeAdapter();
        GsonBuilder gsonBuilder = new GsonBuilder().registerTypeAdapter(NodeTree.class, nodeAdapter);
        Gson gson = gsonBuilder.create();
        gson.toJson(tree); // JSON string
cesarsotovalero
  • 1,116
  • 6
  • 15
1

Will is right, the problem is, that there is an infinite path to save. You can solve this problem by not saving the parent. You can set the parent when you read from the JSON-object. To avoid saving the parent, just set it as transient.

public class TreeNode {
private transient TreeNode parent;
private ArrayList<TreeNode> children;
private Object value;
    //methods
}

When converting back to a POJO, you call the setParent-method below.

TreeNode newRoot = gson.fromJson(treeJson, TreeNode.class);
setParents(newRoot);

The setParents-method just goes through all children of a node and sets their parents to the node which has the node as a child.

private void setParents(TreeNode root) {
        for (TreeNode  node : root.getChildren()) {
            node.setParent(root);
            setParents(node);
        }
    }
Tobii
  • 81
  • 2
1

The member parent points to its parent which has a child pointing to the child which has a parent pointing to the parent ...

Will
  • 73,905
  • 40
  • 169
  • 246