JSON serializer fixes
* Removed redundant module name prefixes to comply with RFC 7951
* Added the top level JSON node, which was missing from RESTCONF's GET output
Change-Id: I0dbe37cec23086511516e4c0c504f609a765d349
diff --git a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DataNodeJsonVisitor.java b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DataNodeJsonVisitor.java
index 617f871..64120c1 100644
--- a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DataNodeJsonVisitor.java
+++ b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DataNodeJsonVisitor.java
@@ -40,7 +40,7 @@
/**
* Creates an instance of data node JSON visitor.
*
- * @param jb json builder
+ * @param jb json builder
* @param context yang serializer context
*/
public DataNodeJsonVisitor(JsonBuilder jb, YangSerializerContext context) {
@@ -51,7 +51,7 @@
@Override
public void enterDataNode(DataNode dataNode,
DataNodeSiblingPositionType siblingType) {
- String nodeName = getNodeNameWithModuleName(dataNode.key().schemaId());
+ String nodeName = getNodeName(dataNode);
switch (dataNode.type()) {
case SINGLE_INSTANCE_NODE:
jsonBuilder.addNodeTopHalf(nodeName, JsonNodeType.OBJECT);
@@ -79,17 +79,22 @@
default:
break;
}
+ jsonBuilder.pushModuleName(getModuleNameFromDataNode(dataNode));
}
- private String getNodeNameWithModuleName(SchemaId schemaId) {
+ private String getModuleNameFromDataNode(DataNode dataNode) {
+ String nameSpace = dataNode.key().schemaId().namespace();
+ return getModuleNameFromNameSpace(jsonSerializerContext, nameSpace);
+ }
+
+ private String getNodeName(DataNode dataNode) {
+ SchemaId schemaId = dataNode.key().schemaId();
String nodeName = schemaId.name();
- String nameSpace = schemaId.namespace();
- String moduleName = getModuleNameFromNameSpace(jsonSerializerContext,
- nameSpace);
+ String moduleName = getModuleNameFromDataNode(dataNode);
StringBuilder builder = new StringBuilder();
- if (nameSpace != null) {
+ if (moduleName != null && !moduleName.equals(jsonBuilder.subTreeModuleName())) {
builder.append(moduleName);
builder.append(COLON);
}
@@ -126,5 +131,6 @@
default:
break;
}
+ jsonBuilder.popModuleName();
}
}
diff --git a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DefaultJsonBuilder.java b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DefaultJsonBuilder.java
index 3dc0ff5..3b624d2 100644
--- a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DefaultJsonBuilder.java
+++ b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DefaultJsonBuilder.java
@@ -25,6 +25,7 @@
import java.io.IOException;
import java.util.Set;
+import java.util.Stack;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.isNullOrEmpty;
@@ -42,15 +43,19 @@
private static final String COMMA = ",";
private static final String COLON = ":";
private static final String QUOTE = "\"";
+ private static final String ROOT_MODULE_NAME = "ROOT";
+ private Stack<String> moduleNameStack;
public DefaultJsonBuilder(String rootName) {
checkNotNull(rootName);
- this.treeString = new StringBuilder(rootName);
+ treeString = new StringBuilder(rootName);
+ moduleNameStack = new Stack<>();
}
public DefaultJsonBuilder() {
- this.treeString = new StringBuilder();
+ treeString = new StringBuilder();
+ moduleNameStack = new Stack<>();
}
@Override
@@ -141,8 +146,6 @@
@Override
public String getTreeString() {
- removeCommaIfExist();
- removeFirstFieldNameIfExist();
return treeString.toString();
}
@@ -178,8 +181,44 @@
}
@Override
- public void removeExtraTerminator() {
+ public String subTreeModuleName() {
+ return moduleNameStack.peek();
+ }
+
+ @Override
+ public void pushModuleName(String moduleName) {
+ moduleNameStack.push(moduleName);
+ }
+
+ @Override
+ public void popModuleName() {
+ moduleNameStack.pop();
+ }
+
+ @Override
+ public void initializeJson() {
+ if (!moduleNameStack.empty()) {
+ moduleNameStack.removeAllElements();
+ }
+ moduleNameStack.push(ROOT_MODULE_NAME);
+ treeString.setLength(0);
+ treeString.append(LEFT_BRACE);
+ }
+
+ @Override
+ public void finalizeJson(boolean isRootTypeMultiInstance) {
removeCommaIfExist();
+
+ if (isRootTypeMultiInstance) {
+ /*
+ * If the root node of the JSON tree is an array
+ * type, we need to close the array with the right
+ * bracket.
+ */
+ treeString.append(RIGHT_BRACKET);
+ }
+
+ treeString.append(RIGHT_BRACE);
}
private void appendField(String fieldName) {
diff --git a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/EncoderUtils.java b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/EncoderUtils.java
index 2f9ca39..35fd26d 100644
--- a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/EncoderUtils.java
+++ b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/EncoderUtils.java
@@ -52,19 +52,21 @@
* Converts a data node to JSON data.
*
* @param dataNode given data node
- * @param context jsonserializer context
+ * @param context jsonserializer context
* @return JSON
*/
public static ObjectNode convertDataNodeToJson(DataNode dataNode, YangSerializerContext context) {
checkNotNull(dataNode, "data node cannot be null");
JsonBuilder jsonBuilder = new DefaultJsonBuilder();
+ jsonBuilder.initializeJson();
DataNodeVisitor treeNodeListener = new DataNodeJsonVisitor(jsonBuilder, context);
- DataNodeSiblingPositionType siblingType = NOT_MULTI_INSTANCE_NODE;
+ DataNodeSiblingPositionType siblingType = (dataNode.type() == MULTI_INSTANCE_NODE) ?
+ FIRST_INSTANCE : NOT_MULTI_INSTANCE_NODE;
walkDataNodeTree(treeNodeListener, dataNode, siblingType);
- jsonBuilder.removeExtraTerminator();
+ jsonBuilder.finalizeJson((dataNode.type() == MULTI_INSTANCE_NODE) ? true : false);
ObjectNode resultData = jsonBuilder.getTreeNode();
return resultData;
}
@@ -201,15 +203,15 @@
Iterator it = childrenList.entrySet().iterator();
while (it.hasNext()) {
- DataNode dataNode = ((Map.Entry<NodeKey, DataNode>) it.next()).getValue();
- String nodeName = dataNode.key().schemaId().name();
- List<DataNode> group = groupedBucket.get(nodeName);
- if (group == null) {
- group = new ArrayList<>();
- groupedBucket.put(nodeName, group);
- }
+ DataNode dataNode = ((Map.Entry<NodeKey, DataNode>) it.next()).getValue();
+ String nodeName = dataNode.key().schemaId().name();
+ List<DataNode> group = groupedBucket.get(nodeName);
+ if (group == null) {
+ group = new ArrayList<>();
+ groupedBucket.put(nodeName, group);
+ }
- group.add(dataNode);
+ group.add(dataNode);
}
diff --git a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/JsonBuilder.java b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/JsonBuilder.java
index cb53cdd..604a6d9 100644
--- a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/JsonBuilder.java
+++ b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/JsonBuilder.java
@@ -88,5 +88,46 @@
*/
ObjectNode getTreeNode();
- void removeExtraTerminator();
+ /**
+ * Returns the YANG module name of the JSON subtree that the builder
+ * is currently building. The YANG module name represents the name
+ * space of the subtree.
+ *
+ * @return YANG module name
+ */
+ String subTreeModuleName();
+
+ /**
+ * Updates the YANG module name of the JSON subtree that the builder
+ * is currently building. The YANG module name represents the name
+ * space of the subtree. This function may be called when the builder
+ * starts to build a data node.
+ *
+ * @param moduleName YANG module name of the current subtree
+ */
+ void pushModuleName(String moduleName);
+
+ /**
+ * Removes the YANG module name of the JSON subtree that the builder
+ * is currently building. This function may be called when the builder
+ * finishes building a data node.
+ */
+ void popModuleName();
+
+ /**
+ * Initializes the output JSON and emits the JSON starting symbol
+ * (e.g., the left curly bracket). This method should be the first method
+ * to be called when a JSON building process starts.
+ */
+ void initializeJson();
+
+ /**
+ * Finalizes the output JSON and emits the JSON terminating symbol
+ * (e.g., the right curly bracket). This method should be the last method
+ * to be called when a JSON building process finishes.
+ *
+ * @param isRootTypeMultiInstance true if the root node of the JSON
+ * tree has the multi-instance node type
+ */
+ void finalizeJson(boolean isRootTypeMultiInstance);
}
diff --git a/serializers/json/src/test/java/org/onosproject/yang/serializers/json/JsonSerializerTest.java b/serializers/json/src/test/java/org/onosproject/yang/serializers/json/JsonSerializerTest.java
index aae2025..f32cdfb 100644
--- a/serializers/json/src/test/java/org/onosproject/yang/serializers/json/JsonSerializerTest.java
+++ b/serializers/json/src/test/java/org/onosproject/yang/serializers/json/JsonSerializerTest.java
@@ -49,9 +49,6 @@
import static org.junit.Assert.assertEquals;
-//import org.onosproject.yang.model.DataNode.Type;
-//import org.onosproject.yang.model.InnerNode;
-//import org.onosproject.yang.model.LeafNode;
/**
* Unit Test for Json Serializer.
@@ -69,82 +66,6 @@
jsonSerializer = new JsonSerializer();
}
-// @Test
-// public void jsonSerializerTest() {
-// String path = "src/test/resources/testinput.json";
-// DefaultCompositeStream external =
-// new DefaultCompositeStream(null, parseInput(path));
-// CompositeData compositeData = jsonSerializer.decode(external, context);
-// ResourceData resourceData = compositeData.resourceData();
-// DataNode rootNode = resourceData.dataNodes().get(0);
-//
-// // 1. test if rootNode is SINGLE_INSTANCE_NODE.
-// assertEquals(WRONG_TYPE, Type.SINGLE_INSTANCE_NODE, rootNode.type());
-// InnerNode innerNode = (InnerNode) rootNode;
-// Map<NodeKey, DataNode> nodeChilds = innerNode.childNodes();
-// // 2. test if Root Node only have one child.
-// assertEquals(WRONG_STRUCTURE, 1, nodeChilds.size());
-// NodeKey nodeChildKey = convertNameStringToNodeKey("top1", "jsonlist");
-// // 3. test if l1 is the only child of Root Node.
-// assertEquals(WRONG_STRUCTURE, true, nodeChilds.containsKey(nodeChildKey));
-// InnerNode nodeTop1 = (InnerNode) nodeChilds.get(nodeChildKey);
-// Map<NodeKey, DataNode> nodeTop1Childs = nodeTop1.childNodes();
-// // 4. test if top1 contains three childs.
-// assertEquals(WRONG_STRUCTURE, 3, nodeTop1Childs.size());
-// DataNode l1DataNode = getDataNode(nodeTop1Childs, "l1");
-// // 5. test if l1 is multi_instance_node
-// assertEquals(WRONG_TYPE, Type.MULTI_INSTANCE_NODE, l1DataNode.type());
-//
-// InnerNode l1InnerNode = (InnerNode) l1DataNode;
-// Map<NodeKey, DataNode> l1ChildNodes = l1InnerNode.childNodes();
-// DataNode k1DataNode = getDataNode(l1ChildNodes, "k1");
-// LeafNode k1LeafNode = (LeafNode) k1DataNode;
-// DataNode k2DataNode = getDataNode(l1ChildNodes, "k2");
-// LeafNode k2LeafNode = (LeafNode) k2DataNode;
-// DataNode k3DataNode = getDataNode(l1ChildNodes, "k3");
-// LeafNode k3LeafNode = (LeafNode) k3DataNode;
-// // 6. test if k1, k2, k3 are with the right value.
-// assertEquals(WRONG_STRUCTURE, true,
-// k1LeafNode.asString().equals("k1value"));
-// assertEquals(WRONG_STRUCTURE, true,
-// k2LeafNode.asString().equals("k2value"));
-// assertEquals(WRONG_STRUCTURE, true,
-// k3LeafNode.asString().equals("k3value"));
-//
-// // 7. test if c1 is in the right structure.
-// DataNode c1DataNode = getDataNode(l1ChildNodes, "c1");
-// InnerNode c1InnerNode = (InnerNode) c1DataNode;
-// DataNode leafC1DataNode = getDataNode(c1InnerNode.childNodes(), "leaf_c1");
-// LeafNode leafC1LeafNode = (LeafNode) leafC1DataNode;
-// assertEquals(WRONG_STRUCTURE, 1, c1InnerNode.childNodes().size());
-// assertEquals(WRONG_TYPE, Type.SINGLE_INSTANCE_LEAF_VALUE_NODE, leafC1DataNode.type());
-// assertEquals(WRONG_STRUCTURE, true, leafC1LeafNode.asString().equals("c1leaf"));
-//
-// DataNode c2DataNode = getDataNode(nodeTop1Childs, "c2");
-// // 8. test if c2 is single_instance_node.
-// assertEquals(WRONG_TYPE, Type.SINGLE_INSTANCE_NODE, c2DataNode.type());
-//
-// InnerNode c2InnerNode = (InnerNode) c2DataNode;
-// Map<NodeKey, DataNode> c2ChildNodes = c2InnerNode.childNodes();
-// DataNode leaflist1DataNode = getDataNode(c2ChildNodes, "leaflist1");
-// LeafNode leafList1LeafNode = (LeafNode) leaflist1DataNode;
-// // 9. test if leaflist1 is in the right structure.
-// assertEquals(WRONG_TYPE, Type.MULTI_INSTANCE_LEAF_VALUE_NODE, leafList1LeafNode.type());
-//
-// }
-//
-// @Test
-// public void encodeTest() {
-// String path = "src/test/resources/testinput.json";
-// DefaultCompositeStream external =
-// new DefaultCompositeStream(null, parseInput(path));
-// CompositeData compositeData = jsonSerializer.decode(external, context);
-// CompositeStream compositeStream = jsonSerializer.encode(compositeData, context);
-// InputStream inputStream = compositeStream.resourceData();
-// String expectString = parseJsonToString(path);
-// assertEquals(WRONG_STRUCTURE, expectString, convertInputStreamToString(inputStream));
-// }
-
@Test
public void demo1Test() {
String path = "src/test/resources/test.json";
@@ -156,6 +77,7 @@
ResourceId rid = resourceData.resourceId();
DataNode rootNode = resourceData.dataNodes().get(0);
+
// encode
RuntimeContext.Builder runtimeContextBuilder = DefaultRuntimeContext.builder();
runtimeContextBuilder.setDataFormat("JSON");
@@ -283,7 +205,7 @@
/**
* Converts name and name space to NodeKey.
*
- * @param name name
+ * @param name name
* @param nameSpace name space
* @return NodeKey
*/
@@ -324,7 +246,7 @@
* Obtain the DataNode for Map with specific name.
*
* @param nodeChils Map container of InnerNode
- * @param keyName name of the DataNode which also shown in Json File.
+ * @param keyName name of the DataNode which also shown in Json File.
* @return DataNode
*/
private DataNode getDataNode(Map<NodeKey, DataNode> nodeChils, String keyName) {