Gson Deserialize to Java Tree
- by MountainX
I need to deserialize some JSON to a Java tree structure that contains TreeNodes and NodeData. TreeNodes are thin wrappers around NodeData. I'll provide the JSON and the classes below. I have looked at the usual Gson help sources, including here, but I can't seem to come up with the solution.
Serialization works fine with Gson. The JSON below was produced by Gson. But deserialization is the problem I need help with. Can someone show me how to write the deserializer (or suggest an alternative approach using Gson best practices)?
Here is my JSON. The "data" element corresponds to class NodeData, and the "subList" JSON element corresponds to Java class TreeNode.
{
"data": {
"version": "032",
"name": "root",
"path": "/",
"id": "1",
"parentId": "0",
"toolTipText": "rootNode"
},
"subList": [
{
"data": {
"version": "032",
"name": "level1",
"labelText": "Some Label Text at Level1",
"path": "/root",
"id": "2",
"parentId": "1",
"toolTipText": "a tool tip for level1"
},
"subList": [
{
"data": {
"version": "032",
"name": "level1_1",
"labelText": "Label level1_1",
"path": "/root/level1",
"id": "3",
"parentId": "2",
"toolTipText": "ToolTipText for level1_1"
}
},
{
"data": {
"version": "032",
"name": "level1_2",
"labelText": "Label level1_2",
"path": "/root/level1",
"id": "4",
"parentId": "2",
"toolTipText": "ToolTipText for level1_2"
}
}
]
},
{
"data": {
"version": "032",
"name": "level2",
"path": "/root",
"id": "5",
"parentId": "1",
"toolTipText": "ToolTipText for level2"
},
"subList": [
{
"data": {
"version": "032",
"name": "level2_1",
"labelText": "Label level2_1",
"path": "/root/level2",
"id": "6",
"parentId": "5",
"toolTipText": "ToolTipText for level2_1"
},
"subList": [
{
"data": {
"version": "032",
"name": "level2_1_1",
"labelText": "Label level2_1_1",
"path": "/root/level2/level2_1",
"id": "7",
"parentId": "6",
"toolTipText": "ToolTipText for level2_1_1"
}
}
]
}
]
}
]
}
Here are the Java classes:
public class Tree {
private TreeNode rootElement;
private HashMap<String, TreeNode> indexById;
private HashMap<String, TreeNode> indexByKey;
private long nextAvailableID = 0;
public Tree() {
indexById = new HashMap<String, TreeNode>();
indexByKey = new HashMap<String, TreeNode>();
}
public long getNextAvailableID()
{
return this.nextAvailableID;
}
...
[snip]
...
}
public class TreeNode {
private Tree tree;
private NodeData data;
public List<TreeNode> subList;
private HashMap<String, TreeNode> indexById;
private HashMap<String, TreeNode> indexByKey;
//this default ctor is used only for Gson deserialization
public TreeNode() {
this.tree = new Tree();
indexById = tree.getIdIndex();
indexByKey = tree.getKeyIndex();
this.makeRoot();
tree.setRootElement(this);
}
//makes this node the root node. Calling this obviously has side effects.
public NodeData makeRoot() {
NodeData rootProp = new NodeData(TreeFactory.version, "example", "rootNode");
String nextAvailableID = getNextAvailableID();
if (!nextAvailableID.equals("1")) {
throw new IllegalStateException();
}
rootProp.setId(nextAvailableID);
rootProp.setParentId("0");
rootProp.setKeyPathOnly("/");
rootProp.setSchema(tree);
this.data = rootProp;
rootProp.setNode(this);
indexById.put(rootProp.getId(), this);
indexByKey.put(rootProp.getKeyFullName(), this);
return rootProp;
}
...
[snip]
...
}
public class NodeData {
protected static Tree tree;
private LinkedHashMap<String, String> keyValMap;
protected String version;
protected String name;
protected String labelText;
protected String path;
protected String id;
protected String parentId;
protected TreeNode node;
protected String toolTipText;//tool tip or help string
protected String imagePath;//for things like images; not persisted to properties
protected static final String delimiter = "/";
//this default ctor is used only for Gson deserialization
public NodeData() {
this("NOT_SET", "NOT_SET", "NOT_SET");
}
...
[snip]
...
}
Side note: The tree data structure is a bit strange, as it includes indexes. Obviously, this isn't a typical search tree. In fact, the tree is used mainly to create a hierarchical path element (String) in each NodeData element. (Example: "path": "/root/level2/level2_1".) The indexes are actually used for NodeData retrieval.