Merge "JSON serializer implementation"
diff --git a/pom.xml b/pom.xml
index 9483615..91c9de8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -37,6 +37,7 @@
<module>compiler</module>
<module>model</module>
<module>runtime</module>
+ <module>serializers</module>
</modules>
<!-- FIXME this can be removed if/when buck-api is released -->
diff --git a/serializers/json/pom.xml b/serializers/json/pom.xml
new file mode 100644
index 0000000..7a9fc74
--- /dev/null
+++ b/serializers/json/pom.xml
@@ -0,0 +1,107 @@
+<!--
+ ~ Copyright 2017-present Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-yang-serializers</artifactId>
+ <version>1.12-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-yang-serializers-json</artifactId>
+ <packaging>jar</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.4</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.7.21</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-yang-model</artifactId>
+ <version>1.12-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-yang-runtime</artifactId>
+ <version>1.12-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <version>2.8.6</version>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-annotations</artifactId>
+ <version>2.8.6</version>
+ </dependency>
+ <!-- TODO: remove
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-yang-compiler-api</artifactId>
+ <version>1.12-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-yang-compiler-datamodel</artifactId>
+ <version>1.12-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-yang-compiler-plugin-utils</artifactId>
+ <version>1.12-SNAPSHOT</version>
+ </dependency>
+ -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>RELEASE</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>3.0.2</version>
+ <configuration>
+ <skipIfEmpty>true</skipIfEmpty>
+ </configuration>
+ <executions>
+ <execution>
+ <id>default</id>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
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
new file mode 100644
index 0000000..c4fcaf6
--- /dev/null
+++ b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DataNodeJsonVisitor.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.yang.serializers.json;
+
+import com.fasterxml.jackson.databind.node.JsonNodeType;
+import org.onosproject.yang.model.DataNode;
+import org.onosproject.yang.model.LeafNode;
+import org.onosproject.yang.model.SchemaId;
+
+import static org.onosproject.yang.serializers.json.DataNodeSiblingPositionType.FIRST_INSTANCE;
+import static org.onosproject.yang.serializers.json.DataNodeSiblingPositionType.LAST_INSTANCE;
+import static org.onosproject.yang.serializers.json.DataNodeSiblingPositionType.SINGLE_INSTANCE_IN_MULTI_NODE;
+
+public class DataNodeJsonVisitor implements DataNodeVisitor {
+ private static final String COLON = ":";
+
+ private JsonBuilder jsonBuilder;
+
+ public DataNodeJsonVisitor(JsonBuilder jsonBuilder) {
+ this.jsonBuilder = jsonBuilder;
+ }
+
+ @Override
+ public void enterDataNode(DataNode dataNode,
+ DataNodeSiblingPositionType siblingType) {
+ String nodeName = getNodeNameWithNamespace(dataNode.key().schemaId());
+ switch (dataNode.type()) {
+ case SINGLE_INSTANCE_NODE:
+ jsonBuilder.addNodeTopHalf(nodeName, JsonNodeType.OBJECT);
+ break;
+ case MULTI_INSTANCE_NODE:
+ if (siblingType == FIRST_INSTANCE ||
+ siblingType == SINGLE_INSTANCE_IN_MULTI_NODE) {
+ jsonBuilder.addNodeTopHalf(nodeName, JsonNodeType.ARRAY);
+ }
+ jsonBuilder.addNodeTopHalf("", JsonNodeType.OBJECT);
+ break;
+ case SINGLE_INSTANCE_LEAF_VALUE_NODE:
+ jsonBuilder.addNodeWithValueTopHalf(nodeName,
+ ((LeafNode) dataNode).value().toString());
+ break;
+ case MULTI_INSTANCE_LEAF_VALUE_NODE:
+ if (siblingType == FIRST_INSTANCE ||
+ siblingType == SINGLE_INSTANCE_IN_MULTI_NODE) {
+ jsonBuilder.addNodeTopHalf(nodeName, JsonNodeType.ARRAY);
+ }
+ jsonBuilder.addValueToLeafListNode(((LeafNode) dataNode).value().toString());
+ break;
+ default:
+ break;
+ }
+ }
+
+ private String getNodeNameWithNamespace(SchemaId schemaId) {
+ String nodeName = schemaId.name();
+ String nameSpace = schemaId.namespace();
+
+ StringBuilder builder = new StringBuilder();
+
+ builder.append(nodeName);
+
+ if (nameSpace != null) {
+ builder.append(COLON);
+ builder.append(nameSpace);
+ }
+
+ return builder.toString();
+ }
+
+ @Override
+ public void exitDataNode(DataNode dataNode,
+ DataNodeSiblingPositionType siblingType) {
+ switch (dataNode.type()) {
+ case SINGLE_INSTANCE_NODE:
+ jsonBuilder.addNodeBottomHalf(JsonNodeType.OBJECT);
+ break;
+ case MULTI_INSTANCE_NODE:
+ jsonBuilder.addNodeBottomHalf(JsonNodeType.OBJECT);
+ if (siblingType == LAST_INSTANCE ||
+ siblingType == SINGLE_INSTANCE_IN_MULTI_NODE) {
+ jsonBuilder.addNodeBottomHalf(JsonNodeType.ARRAY);
+ }
+ break;
+ case SINGLE_INSTANCE_LEAF_VALUE_NODE:
+ jsonBuilder.addNodeBottomHalf(JsonNodeType.STRING);
+ break;
+ case MULTI_INSTANCE_LEAF_VALUE_NODE:
+ jsonBuilder.addNodeBottomHalf(JsonNodeType.STRING);
+ if (siblingType == LAST_INSTANCE ||
+ siblingType == SINGLE_INSTANCE_IN_MULTI_NODE) {
+ jsonBuilder.addNodeBottomHalf(JsonNodeType.ARRAY);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DataNodeSiblingPositionType.java b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DataNodeSiblingPositionType.java
new file mode 100644
index 0000000..f88aeeb
--- /dev/null
+++ b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DataNodeSiblingPositionType.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.yang.serializers.json;
+
+/**
+ * Denotation of the relative position of a multi-instance data node
+ * relative to its siblings.
+ */
+public enum DataNodeSiblingPositionType {
+ /**
+ * Denotes that the given node is not a multi-instance or leaf-list node.
+ */
+ NOT_MULTI_INSTANCE_NODE,
+
+ /**
+ * Denotes that the node is the first instance in the sibling list.
+ */
+ FIRST_INSTANCE,
+
+ /**
+ * Denotes that the node is the last instance in the sibling list.
+ */
+ LAST_INSTANCE,
+
+ /**
+ * Denotes that the node is one of the middle instances.
+ */
+ MIDDLE_INSTANCE,
+
+ /**
+ * Denotes that the given node is the only instance in the multi-instance
+ * data node.
+ */
+ SINGLE_INSTANCE_IN_MULTI_NODE,
+ /**
+ * Used for error case or as uninitialized data.
+ */
+ UNKNOWN_TYPE,
+}
diff --git a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DataNodeVisitor.java b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DataNodeVisitor.java
new file mode 100644
index 0000000..1d4f59b
--- /dev/null
+++ b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DataNodeVisitor.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.yang.serializers.json;
+
+import org.onosproject.yang.model.DataNode;
+
+/**
+ * Representation of the visitor to data nodes during the
+ * data tree traversal.
+ */
+public interface DataNodeVisitor {
+ /**
+ * Enters the data node by the visitor. The function is called by
+ * the walker of the data tree when it enters the given data node.
+ * The visitor uses this function to process the data of this node.
+ *
+ * @param dataNode data node which the tree walker visits
+ * @param siblingType indicates whether the data node the the first
+ * or last sibling instance of a multi-instance node
+ */
+ void enterDataNode(DataNode dataNode, DataNodeSiblingPositionType siblingType);
+
+ /**
+ * Exits the data node by the visitor. The function is called by
+ * the walker of the data tree when it's about to leave the given
+ * data node. The visitor uses this function to run cleanup work
+ * of the data processing.
+ *
+ * @param dataNode data node which the tree walker finishes the visit
+ * @param siblingType indicates whether the data node the the first
+ * or last sibling instance of a multi-instance node
+ */
+ void exitDataNode(DataNode dataNode, DataNodeSiblingPositionType siblingType);
+}
diff --git a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DecoderUtils.java b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DecoderUtils.java
new file mode 100644
index 0000000..753b699
--- /dev/null
+++ b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DecoderUtils.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.yang.serializers.json;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+import org.onosproject.yang.model.DataNode;
+import org.onosproject.yang.model.ResourceId;
+import org.onosproject.yang.runtime.YangSerializerContext;
+import org.onosproject.yang.runtime.helperutils.SerializerHelper;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+
+/**
+ * Utilities for parsing URI and JSON strings.
+ */
+public final class DecoderUtils {
+ private static final Splitter SLASH_SPLITTER = Splitter.on('/');
+ private static final Splitter COMMA_SPLITTER = Splitter.on(',');
+ private static final String EQUAL = "=";
+ private static final String COMMA = ",";
+ private static final String COLON = ":";
+ private static final String SLASH = "/";
+ private static final String URI_ENCODING_CHAR_SET = "ISO-8859-1";
+ private static final String ERROR_LIST_MSG = "List/Leaf-list node should be " +
+ "in format \"nodeName=key\"or \"nodeName=instance-value\"";
+
+
+ // no instantiation
+ private DecoderUtils() {
+ }
+
+ /**
+ * Converts a URI string to resource identifier.
+ *
+ * @param uriString given URI
+ * @param context YANG schema context information
+ * @return resource ID
+ */
+ public static ResourceId convertUriToRid(String uriString,
+ YangSerializerContext context) {
+ if (uriString == null || uriString.isEmpty()) {
+ return null;
+ }
+
+ List<String> paths = urlPathArgsDecode(SLASH_SPLITTER.split(uriString));
+
+ if (!paths.isEmpty()) {
+ ResourceId.Builder ridBuilder =
+ SerializerHelper.initializeResourceId(context);
+ processPathSegments(paths, ridBuilder);
+ return ridBuilder.build();
+ }
+
+ return null;
+ }
+
+ /**
+ * Converts JSON data to a data node. This method should be used when
+ * the URI corresponding to the JSON body is null (Thus the caller can
+ * only provide a serializer context rather than a resource ID).
+ *
+ * @param rootNode given JSON data
+ * @param context YANG serializer context corresponding
+ * to the target data node
+ * @return data node
+ */
+ public static DataNode convertJsonToDataNode(ObjectNode rootNode,
+ YangSerializerContext context) {
+ if (rootNode == null || context == null) {
+ return null;
+ }
+
+ DataNode.Builder dataNodeBuilder = SerializerHelper.
+ initializeDataNode(context);
+
+ JsonWalker jsonWalker = new DefaultJsonWalker(dataNodeBuilder);
+ jsonWalker.walkJsonNode(null, rootNode);
+
+ return dataNodeBuilder.build();
+ }
+
+ /**
+ * Converts JSON data to a data node. This method should be used when
+ * the JSON body has a valid URI associated with it (so that the caller
+ * can convert the URI to a resource ID).
+ *
+ * @param rootNode given JSON data
+ * @param ridBuilder resource ID builder corresponding
+ * to the target data node
+ * @return data node
+ */
+ public static DataNode convertJsonToDataNode(ObjectNode rootNode,
+ ResourceId.Builder ridBuilder) {
+ if (rootNode == null || ridBuilder == null) {
+ return null;
+ }
+
+ DataNode.Builder dataNodeBuilder = SerializerHelper.
+ initializeDataNode(ridBuilder);
+
+ JsonWalker jsonWalker = new DefaultJsonWalker(dataNodeBuilder);
+ jsonWalker.walkJsonNode(null, rootNode);
+
+ return dataNodeBuilder.build();
+ }
+
+ /**
+ * Converts a list of path from the original format to ISO-8859-1 code.
+ *
+ * @param paths the original paths
+ * @return list of decoded paths
+ */
+ public static List<String> urlPathArgsDecode(Iterable<String> paths) {
+ try {
+ List<String> decodedPathArgs = new ArrayList<>();
+ for (String pathArg : paths) {
+ String decode = URLDecoder.decode(pathArg,
+ URI_ENCODING_CHAR_SET);
+ decodedPathArgs.add(decode);
+ }
+ return decodedPathArgs;
+ } catch (UnsupportedEncodingException e) {
+ throw new SerializerException("Invalid URL path arg '" +
+ paths + "': ", e);
+ }
+ }
+
+ private static ResourceId.Builder processPathSegments(List<String> paths,
+ ResourceId.Builder builder) {
+ if (paths.isEmpty()) {
+ return builder;
+ }
+
+ boolean isLastSegment = paths.size() == 1;
+
+ String segment = paths.iterator().next();
+ processSinglePathSegment(segment, builder);
+
+ if (isLastSegment) {
+ // We have hit the base case of recursion.
+ return builder;
+ }
+
+ /*
+ * Chop off the first segment, and recursively process the rest
+ * of the path segments.
+ */
+ List<String> remainPaths = paths.subList(1, paths.size());
+ processPathSegments(remainPaths, builder);
+
+ return builder;
+ }
+
+ private static void processSinglePathSegment(String pathSegment,
+ ResourceId.Builder builder) {
+ if (pathSegment.contains(COLON)) {
+ processPathSegmentWithNamespace(pathSegment, builder);
+ } else {
+ processPathSegmentWithoutNamespace(pathSegment, builder);
+ }
+ }
+
+ private static void processPathSegmentWithNamespace(String pathSegment,
+ ResourceId.Builder builder) {
+
+ String nodeName = getLatterSegment(pathSegment, COLON);
+ String namespace = getPreSegment(pathSegment, COLON);
+ addNodeNameToRid(nodeName, namespace, builder);
+ }
+
+ private static void processPathSegmentWithoutNamespace(String nodeName,
+ ResourceId.Builder builder) {
+ addNodeNameToRid(nodeName, null, builder);
+ }
+
+ private static void addNodeNameToRid(String nodeName,
+ String namespace,
+ ResourceId.Builder builder) {
+ if (nodeName.contains(EQUAL)) {
+ addListOrLeafList(nodeName, namespace, builder);
+ } else {
+ addLeaf(nodeName, namespace, builder);
+ }
+ }
+
+ private static void addListOrLeafList(String path,
+ String namespace,
+ ResourceId.Builder builder) {
+ String nodeName = getPreSegment(path, EQUAL);
+ String keyStr = getLatterSegment(path, EQUAL);
+ if (keyStr == null) {
+ throw new SerializerException(ERROR_LIST_MSG);
+ }
+
+ if (keyStr.contains(COMMA)) {
+ List<String> keys = Lists.
+ newArrayList(COMMA_SPLITTER.split(keyStr));
+ SerializerHelper.addToResourceId(builder, nodeName, namespace, keys);
+ } else {
+ SerializerHelper.addToResourceId(builder, nodeName, namespace,
+ Lists.newArrayList(keyStr));
+ }
+ }
+
+ private static void addLeaf(String nodeName,
+ String namespace,
+ ResourceId.Builder builder) {
+ checkNotNull(nodeName);
+ SerializerHelper.addToResourceId(builder, nodeName, namespace, "");
+ }
+
+ /**
+ * Returns the previous segment of a path which is separated by a split char.
+ * For example:
+ * <pre>
+ * "foo:bar", ":" --> "foo"
+ * </pre>
+ *
+ * @param path the original path string
+ * @param splitChar char used to split the path
+ * @return the previous segment of the path
+ */
+ private static String getPreSegment(String path, String splitChar) {
+ int idx = path.indexOf(splitChar);
+ if (idx == -1) {
+ return null;
+ }
+
+ if (path.indexOf(splitChar, idx + 1) != -1) {
+ return null;
+ }
+
+ return path.substring(0, idx);
+ }
+
+ /**
+ * Returns the latter segment of a path which is separated by a split char.
+ * For example:
+ * <pre>
+ * "foo:bar", ":" --> "bar"
+ * </pre>
+ *
+ * @param path the original path string
+ * @param splitChar char used to split the path
+ * @return the latter segment of the path
+ */
+ private static String getLatterSegment(String path, String splitChar) {
+ int idx = path.indexOf(splitChar);
+ if (idx == -1) {
+ return path;
+ }
+
+ if (path.indexOf(splitChar, idx + 1) != -1) {
+ return null;
+ }
+
+ return path.substring(idx + 1);
+ }
+}
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
new file mode 100644
index 0000000..4344df5
--- /dev/null
+++ b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DefaultJsonBuilder.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.yang.serializers.json;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Strings.isNullOrEmpty;
+
+/**
+ * Represents implementation of interfaces to build and obtain JSON data tree.
+ */
+public class DefaultJsonBuilder implements JsonBuilder {
+ private Logger log = LoggerFactory.getLogger(getClass());
+ private StringBuilder treeString;
+ private static final String LEFT_BRACE = "{";
+ private static final String RIGHT_BRACE = "}";
+ private static final String LEFT_BRACKET = "[";
+ private static final String RIGHT_BRACKET = "]";
+ private static final String COMMA = ",";
+ private static final String COLON = ":";
+ private static final String QUOTE = "\"";
+
+
+ public DefaultJsonBuilder(String rootName) {
+ checkNotNull(rootName);
+ this.treeString = new StringBuilder(rootName);
+ }
+
+ public DefaultJsonBuilder() {
+ this.treeString = new StringBuilder();
+ }
+
+ @Override
+ public void addNodeTopHalf(String nodeName, JsonNodeType nodeType) {
+ appendField(nodeName);
+
+ switch (nodeType) {
+ case OBJECT:
+ treeString.append(LEFT_BRACE);
+ break;
+ case ARRAY:
+ treeString.append(LEFT_BRACKET);
+ break;
+ default:
+ log.error("Unknown support type {} for this method.", nodeType);
+ }
+
+ }
+
+ @Override
+ public void addNodeWithValueTopHalf(String nodeName, String value) {
+ if (isNullOrEmpty(nodeName)) {
+ return;
+ }
+ appendField(nodeName);
+ if (value.isEmpty()) {
+ return;
+ }
+ treeString.append(QUOTE);
+ treeString.append(value);
+ treeString.append(QUOTE);
+ treeString.append(COMMA);
+ }
+
+ @Override
+ public void addNodeWithSetTopHalf(String nodeName, Set<String> sets) {
+ if (isNullOrEmpty(nodeName)) {
+ return;
+ }
+ appendField(nodeName);
+ treeString.append(LEFT_BRACKET);
+ for (String el : sets) {
+ treeString.append(QUOTE);
+ treeString.append(el);
+ treeString.append(QUOTE);
+ treeString.append(COMMA);
+ }
+ }
+
+ @Override
+ public void addValueToLeafListNode(String value) {
+ if (isNullOrEmpty(value)) {
+ return;
+ }
+
+ treeString.append(QUOTE);
+ treeString.append(value);
+ treeString.append(QUOTE);
+ treeString.append(COMMA);
+ }
+
+ @Override
+ public void addNodeBottomHalf(JsonNodeType nodeType) {
+
+ switch (nodeType) {
+ case OBJECT:
+ removeCommaIfExist();
+ treeString.append(RIGHT_BRACE);
+ treeString.append(COMMA);
+ break;
+ case ARRAY:
+ removeCommaIfExist();
+ treeString.append(RIGHT_BRACKET);
+ treeString.append(COMMA);
+ break;
+ case BINARY:
+ case BOOLEAN:
+ case MISSING:
+ case NULL:
+ case NUMBER:
+ case POJO:
+ case STRING:
+ break;
+ default:
+ log.info("Unknown json node type {}", nodeType);
+ }
+ }
+
+ @Override
+ public String getTreeString() {
+ removeCommaIfExist();
+ removeFirstFieldNameIfExist();
+ return treeString.toString();
+ }
+
+ private void removeFirstFieldNameIfExist() {
+ int index1 = treeString.indexOf(LEFT_BRACE);
+ int index2 = treeString.indexOf(LEFT_BRACKET);
+ if (index1 < 0 && index2 < 0) {
+ return;
+ }
+ int index;
+
+ if (index1 < 0) {
+ index = index2;
+ } else if (index2 < 0) {
+ index = index1;
+ } else {
+ index = (index1 < index2) ? index1 : index2;
+ }
+ treeString.delete(0, index);
+ }
+
+ @Override
+ public ObjectNode getTreeNode() {
+ ObjectNode node = null;
+ try {
+ node = (ObjectNode) (new ObjectMapper()).readTree(getTreeString());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return node;
+ }
+
+ private void appendField(String fieldName) {
+ if (fieldName != null && !fieldName.isEmpty()) {
+ treeString.append(QUOTE);
+ treeString.append(fieldName);
+ treeString.append(QUOTE);
+ treeString.append(COLON);
+ }
+ }
+
+ private void removeCommaIfExist() {
+ if (treeString.charAt(treeString.length() - 1) == COMMA.charAt(0)) {
+ treeString.deleteCharAt(treeString.length() - 1);
+ }
+ }
+}
diff --git a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DefaultJsonWalker.java b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DefaultJsonWalker.java
new file mode 100644
index 0000000..a4edcab
--- /dev/null
+++ b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/DefaultJsonWalker.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.yang.serializers.json;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
+import org.onosproject.yang.model.DataNode;
+import org.onosproject.yang.runtime.helperutils.SerializerHelper;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import static org.onosproject.yang.model.DataNode.Type.MULTI_INSTANCE_LEAF_VALUE_NODE;
+import static org.onosproject.yang.model.DataNode.Type.MULTI_INSTANCE_NODE;
+import static org.onosproject.yang.model.DataNode.Type.SINGLE_INSTANCE_LEAF_VALUE_NODE;
+import static org.onosproject.yang.model.DataNode.Type.SINGLE_INSTANCE_NODE;
+
+/**
+ * Represents implementation of JSON walk, which walks the JSON object node.
+ */
+public class DefaultJsonWalker implements JsonWalker {
+ private static final String COLON = ":";
+
+ private DataNode.Builder dataNodeBuilder;
+
+ public DefaultJsonWalker(DataNode.Builder dataNodeBuilder) {
+ this.dataNodeBuilder = dataNodeBuilder;
+ }
+
+ @Override
+ public void walkJsonNode(String fieldName, JsonNode jsonNode) {
+
+ JsonNodeType nodeType = jsonNode.getNodeType();
+
+ if (!jsonNode.isContainerNode()) {
+ //the node has no children, so add it as leaf node to the data tree.
+ addLeafNodeToDataTree(fieldName, jsonNode);
+ SerializerHelper.exitDataNode(dataNodeBuilder);
+ return;
+ }
+
+ /*
+ * For an array node, there are 2 cases:
+ *
+ * 1. It is a leaflist node
+ * 2. It is a multi-instance node.
+ */
+ if (jsonNode.isArray()) {
+ // Let's deal with the leaflist case first.
+ if (isJsonNodeLeafList((ArrayNode) jsonNode)) {
+ addLeafListNodeToDataTree(fieldName, (ArrayNode) jsonNode);
+ SerializerHelper.exitDataNode(dataNodeBuilder);
+ return;
+ }
+
+ /*
+ * This is a multi-instance node. Each element in the
+ * array is an instance of multi-instance node in the data tree.
+ */
+ Iterator<JsonNode> elements = jsonNode.elements();
+ while (elements.hasNext()) {
+ JsonNode element = elements.next();
+ addMultiInstanceNodeToDataTree(fieldName);
+
+ // Recursively build the subtree of element
+ walkJsonNode(null, element);
+
+ // We are done with this array element
+ SerializerHelper.exitDataNode(dataNodeBuilder);
+ }
+
+ // We are done with this array node.
+ SerializerHelper.exitDataNode(dataNodeBuilder);
+ return;
+ }
+
+ /*
+ * If we reach here, then this node is an object node. An object node
+ * has a set of name-value pairs. ("value" can be object node.)
+ */
+ if (fieldName != null) {
+ // If fieldName is null, then the caller does not want to
+ // add the node into the data tree. Rather, it just want to add
+ // the children nodes to the current node of the data tree.
+ addSingleInstanceNodeToDataTree(fieldName);
+ }
+
+ Iterator<Map.Entry<String, JsonNode>> fields = jsonNode.fields();
+ while (fields.hasNext()) {
+ //get the children entry of the node
+ Map.Entry<String, JsonNode> currentChild = fields.next();
+ String key = currentChild.getKey();
+ JsonNode value = currentChild.getValue();
+ walkJsonNode(key, value);
+ // NOTE: Don't move up, because walkJsonNode will do so.
+ // SerializerHelper.exitDataNode(dataNodeBuilder);
+ }
+
+ if (fieldName != null) {
+ // move up since we finish creating a container node.
+ SerializerHelper.exitDataNode(dataNodeBuilder);
+ }
+ }
+
+ private void addDataNode(String fieldName, String value, DataNode.Type nodeType) {
+ String nodeName = getLatterSegment(fieldName, COLON);
+ String namespace = getPreSegment(fieldName, COLON);
+ SerializerHelper.addDataNode(dataNodeBuilder,
+ nodeName,
+ namespace,
+ value,
+ nodeType);
+ }
+
+ private void addNoneLeafDataNode(String fieldName, DataNode.Type nodeType) {
+ addDataNode(fieldName, null, nodeType);
+ }
+
+ private void addLeafDataNode(String fieldName, String value, DataNode.Type nodeType) {
+ addDataNode(fieldName, value, nodeType);
+ }
+
+ private void addSingleInstanceNodeToDataTree(String fieldName) {
+ addNoneLeafDataNode(fieldName, SINGLE_INSTANCE_NODE);
+ }
+
+ private void addMultiInstanceNodeToDataTree(String fieldName) {
+ addNoneLeafDataNode(fieldName, MULTI_INSTANCE_NODE);
+ }
+
+ private void addLeafNodeToDataTree(String fieldName, JsonNode jsonNode) {
+ String value = jsonNode.asText();
+ addLeafDataNode(fieldName, value,
+ SINGLE_INSTANCE_LEAF_VALUE_NODE);
+ }
+
+ private void addLeafListNodeToDataTree(String fieldName,
+ ArrayNode jsonNode) {
+ Iterator<JsonNode> elements = jsonNode.elements();
+ while (elements.hasNext()) {
+ JsonNode element = elements.next();
+ JsonNodeType eleType = element.getNodeType();
+
+ if (eleType == JsonNodeType.STRING || eleType == JsonNodeType.NUMBER) {
+ addLeafDataNode(fieldName, element.asText(),
+ MULTI_INSTANCE_LEAF_VALUE_NODE);
+ SerializerHelper.exitDataNode(dataNodeBuilder);
+ }
+ }
+ }
+
+ private boolean isJsonNodeLeafList(ArrayNode jsonNode) {
+ if (!jsonNode.isArray()) {
+ return false;
+ }
+
+ Iterator<JsonNode> elements = jsonNode.elements();
+ while (elements.hasNext()) {
+ JsonNode element = elements.next();
+ JsonNodeType eleType = element.getNodeType();
+ if (eleType != JsonNodeType.STRING && eleType != JsonNodeType.NUMBER) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the previous segment of a path which is separated by a split char.
+ * For example:
+ * <pre>
+ * "foo:bar", ":" --> "foo"
+ * </pre>
+ *
+ * @param path the original path string
+ * @param splitChar char used to split the path
+ * @return the previous segment of the path
+ */
+ private String getPreSegment(String path, String splitChar) {
+ int idx = path.indexOf(splitChar);
+ if (idx == -1) {
+ return null;
+ }
+
+ if (path.indexOf(splitChar, idx + 1) != -1) {
+ return null;
+ }
+
+ return path.substring(0, idx);
+ }
+
+ /**
+ * Returns the latter segment of a path which is separated by a split char.
+ * For example:
+ * <pre>
+ * "foo:bar", ":" --> "bar"
+ * </pre>
+ *
+ * @param path the original path string
+ * @param splitChar char used to split the path
+ * @return the latter segment of the path
+ */
+ private String getLatterSegment(String path, String splitChar) {
+ int idx = path.indexOf(splitChar);
+ if (idx == -1) {
+ return path;
+ }
+
+ if (path.indexOf(splitChar, idx + 1) != -1) {
+ return null;
+ }
+
+ return path.substring(idx + 1);
+ }
+}
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
new file mode 100644
index 0000000..00022e5
--- /dev/null
+++ b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/EncoderUtils.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.yang.serializers.json;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Splitter;
+import org.onosproject.yang.model.DataNode;
+import org.onosproject.yang.model.InnerNode;
+import org.onosproject.yang.model.KeyLeaf;
+import org.onosproject.yang.model.LeafListKey;
+import org.onosproject.yang.model.ListKey;
+import org.onosproject.yang.model.NodeKey;
+import org.onosproject.yang.model.ResourceId;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.yang.model.DataNode.Type.MULTI_INSTANCE_LEAF_VALUE_NODE;
+import static org.onosproject.yang.model.DataNode.Type.MULTI_INSTANCE_NODE;
+import static org.onosproject.yang.model.DataNode.Type.SINGLE_INSTANCE_NODE;
+import static org.onosproject.yang.serializers.json.DataNodeSiblingPositionType.FIRST_INSTANCE;
+import static org.onosproject.yang.serializers.json.DataNodeSiblingPositionType.LAST_INSTANCE;
+import static org.onosproject.yang.serializers.json.DataNodeSiblingPositionType.MIDDLE_INSTANCE;
+import static org.onosproject.yang.serializers.json.DataNodeSiblingPositionType.NOT_MULTI_INSTANCE_NODE;
+import static org.onosproject.yang.serializers.json.DataNodeSiblingPositionType.SINGLE_INSTANCE_IN_MULTI_NODE;
+import static org.onosproject.yang.serializers.json.DataNodeSiblingPositionType.UNKNOWN_TYPE;
+
+/**
+ * Utilities for converting Data Nodes into JSON format.
+ */
+public final class EncoderUtils {
+ private static final Splitter SLASH_SPLITTER = Splitter.on('/');
+ private static final Splitter COMMA_SPLITTER = Splitter.on(',');
+ private static final String EQUAL = "=";
+ private static final String COMMA = ",";
+ private static final String COLON = ":";
+ private static final String SLASH = "/";
+
+ // no instantiation
+ private EncoderUtils() {
+ }
+
+ /**
+ * Converts a resource identifier to URI string.
+ *
+ * @param rid resource identifier
+ * @return URI
+ */
+ public static String convertRidToUri(ResourceId rid) {
+ if (rid == null) {
+ return null;
+ }
+
+ StringBuilder uriBuilder = new StringBuilder();
+ List<NodeKey> nodeKeyList = rid.nodeKeys();
+ String curNameSpace = null;
+ for (NodeKey key : nodeKeyList) {
+ curNameSpace = addNodeKeyToUri(key, curNameSpace, uriBuilder);
+ }
+ return uriBuilder.toString();
+ }
+
+ private static String addNodeKeyToUri(NodeKey key,
+ String curNameSpace,
+ StringBuilder uriBuilder) {
+ String newNameSpace;
+ if (key instanceof LeafListKey) {
+ newNameSpace = addLeafListNodeToUri((LeafListKey) key, curNameSpace, uriBuilder);
+ } else if (key instanceof ListKey) {
+ newNameSpace = addListNodeToUri((ListKey) key, curNameSpace, uriBuilder);
+ } else {
+ uriBuilder.append(SLASH);
+ newNameSpace = addNodeNameToUri(key, curNameSpace, uriBuilder);
+ }
+ return newNameSpace;
+ }
+
+ private static String addLeafListNodeToUri(LeafListKey key,
+ String curNameSpace,
+ StringBuilder uriBuilder) {
+
+ String newNameSpace = addNodeNameToUri(key, curNameSpace, uriBuilder);
+ uriBuilder.append(EQUAL);
+ uriBuilder.append(key.asString());
+ return newNameSpace;
+ }
+
+ private static String addListNodeToUri(ListKey key,
+ String curNameSpace,
+ StringBuilder uriBuilder) {
+ String newNameSpace = addNodeNameToUri(key, curNameSpace, uriBuilder);
+ uriBuilder.append(EQUAL);
+ String prefix = "";
+ for (KeyLeaf keyLeaf : key.keyLeafs()) {
+ uriBuilder.append(prefix);
+ prefix = COMMA;
+ uriBuilder.append(keyLeaf.leafValue().toString());
+ }
+
+ return newNameSpace;
+ }
+
+ private static String addNodeNameToUri(NodeKey key,
+ String curNameSpace,
+ StringBuilder uriBuilder) {
+ String nodeName = key.schemaId().name();
+ String newNameSpace = key.schemaId().namespace();
+
+ uriBuilder.append(nodeName);
+
+ if (newNameSpace == null) {
+ return curNameSpace;
+ }
+
+ if (!newNameSpace.equals(curNameSpace)) {
+ uriBuilder.append(COLON);
+ uriBuilder.append(newNameSpace);
+ }
+
+ return newNameSpace;
+ }
+
+ /**
+ * Converts a data node to JSON data.
+ *
+ * @param dataNode given data node
+ * @return JSON
+ */
+ public static ObjectNode convertDataNodeToJson(DataNode dataNode) {
+ checkNotNull(dataNode, "data node cannot be null");
+
+ JsonBuilder jsonBuilder = new DefaultJsonBuilder();
+ DataNodeVisitor treeNodeListener = new DataNodeJsonVisitor(jsonBuilder);
+
+ DataNodeSiblingPositionType siblingType = NOT_MULTI_INSTANCE_NODE;
+ walkDataNodeTree(treeNodeListener, dataNode, siblingType);
+
+ ObjectNode resultData = jsonBuilder.getTreeNode();
+ return resultData;
+ }
+
+ private static void walkDataNodeTree(DataNodeVisitor dataNodeVisitor,
+ DataNode dataNode,
+ DataNodeSiblingPositionType siblingType) {
+ checkNotNull(dataNode, "data tree cannot be null");
+ checkNotNull(dataNodeVisitor, "dataNodeVisitor cannot be null");
+
+ // depth-first walk of the data node tree
+ dataNodeVisitor.enterDataNode(dataNode, siblingType);
+
+ if (dataNode.type() == SINGLE_INSTANCE_NODE ||
+ dataNode.type() == MULTI_INSTANCE_NODE) {
+ // Walk through every child on the children list
+ walkChildNodeList(dataNodeVisitor, dataNode);
+ }
+
+ dataNodeVisitor.exitDataNode(dataNode, siblingType);
+ }
+
+ private static void walkChildNodeList(DataNodeVisitor dataNodeVisitor,
+ DataNode dataNode) {
+ if (dataNode.type() != SINGLE_INSTANCE_NODE &&
+ dataNode.type() != MULTI_INSTANCE_NODE) {
+ // Only inner nodes could have children.
+ return;
+ }
+
+ Map<NodeKey, DataNode> childrenList = ((InnerNode) dataNode).childNodes();
+ if (childrenList == null || childrenList.isEmpty()) {
+ // the children list is either not yet created or empty.
+ return;
+ }
+
+ /*
+ * We now have a none empty children list to walk through.
+ */
+
+ DataNodeSiblingPositionType prevChildType = UNKNOWN_TYPE;
+ DataNodeSiblingPositionType currChildType = UNKNOWN_TYPE;
+
+ Iterator it = childrenList.entrySet().iterator();
+ DataNode currChild = ((Map.Entry<NodeKey, DataNode>) it.next()).getValue();
+ DataNode nextChild = null;
+ boolean lastChildNotProcessed = true;
+ while (lastChildNotProcessed) {
+ /*
+ * Iterate through the children list. Invoke data node walker
+ * for every child. If the child is a multi-instance node, we
+ * need to determine if it is the first or last sibling and pass
+ * this info the walker.
+ */
+ if (it.hasNext()) {
+ nextChild = ((Map.Entry<NodeKey, DataNode>) it.next()).getValue();
+ } else {
+ /*
+ * Current child is the last child.
+ * So mark this iteration as the last one.
+ */
+ lastChildNotProcessed = false;
+ nextChild = null;
+ }
+ currChildType = getCurrentChildSiblingType(currChild,
+ nextChild,
+ prevChildType);
+ walkDataNodeTree(dataNodeVisitor, currChild, currChildType);
+ prevChildType = currChildType;
+ currChild = nextChild;
+ }
+ }
+
+ private static DataNodeSiblingPositionType getCurrentChildSiblingType(DataNode currChild,
+ DataNode nextChild,
+ DataNodeSiblingPositionType prevChildType) {
+ if (currChild.type() != MULTI_INSTANCE_NODE &&
+ currChild.type() != MULTI_INSTANCE_LEAF_VALUE_NODE) {
+ return NOT_MULTI_INSTANCE_NODE;
+ }
+
+ DataNodeSiblingPositionType curChildSiblingType = UNKNOWN_TYPE;
+ switch (prevChildType) {
+ case UNKNOWN_TYPE:
+ /*
+ * If type of previous child is unknown, that means
+ * the current child is the first sibling. If the next
+ * child is null, then that means the current child is
+ * the only child.
+ */
+ if (nextChild == null) {
+ curChildSiblingType = SINGLE_INSTANCE_IN_MULTI_NODE;
+ } else {
+ curChildSiblingType = FIRST_INSTANCE;
+ }
+ break;
+ case NOT_MULTI_INSTANCE_NODE:
+ case LAST_INSTANCE:
+ curChildSiblingType = FIRST_INSTANCE;
+ break;
+ case FIRST_INSTANCE:
+ case MIDDLE_INSTANCE:
+ /*
+ * If we still have a next child and the next child's name
+ * is the same name as the current node, then the current
+ * node is not the last sibling yet.
+ */
+ if (nextChild != null &&
+ nextChild.key().schemaId().name().
+ equals(currChild.key().schemaId().name())) {
+ curChildSiblingType = MIDDLE_INSTANCE;
+ } else {
+ curChildSiblingType = LAST_INSTANCE;
+ }
+ break;
+ default:
+ curChildSiblingType = UNKNOWN_TYPE;
+ }
+
+ return curChildSiblingType;
+ }
+}
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
new file mode 100644
index 0000000..98f162b
--- /dev/null
+++ b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/JsonBuilder.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.yang.serializers.json;
+
+import com.fasterxml.jackson.databind.node.JsonNodeType;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import java.util.Set;
+
+/**
+ * Abstraction of an entity which provides interfaces to build and obtain JSON
+ * data tree.
+ */
+public interface JsonBuilder {
+
+ /**
+ * Adds a to half(a left brace/bracket and the field name) of a JSON
+ * object/array to the JSON tree. This method is used by protocols which
+ * knows the nature (object/array) of node.
+ *
+ * @param nodeName name of child to be added
+ * @param nodeType the type of the child
+ */
+ void addNodeTopHalf(String nodeName, JsonNodeType nodeType);
+
+ /**
+ * Adds a child with value and a comma to the JSON tree.
+ * Protocols unaware of nature of node (single/multiple) will use it to add
+ * both single instance and multi instance node. Protocols aware of nature
+ * of node will use it for single instance value node addition.
+ *
+ * @param nodeName name of child to be added
+ * @param value value of the child node
+ */
+ void addNodeWithValueTopHalf(String nodeName, String value);
+
+ /**
+ * Adds a child with list of values to JSON data tree. This method is
+ * used by protocols which knows the nature (object/array) of node for
+ * ArrayNode addition.
+ *
+ * @param nodeName name of child to be added
+ * @param sets the value list of the child
+ */
+ void addNodeWithSetTopHalf(String nodeName, Set<String> sets);
+
+ /**
+ * Adds value to a leaf list node.
+ *
+ * @param value value to be added
+ */
+ void addValueToLeafListNode(String value);
+
+ /**
+ * Adds the bottom half(a right brace/bracket) of a JSON
+ * object/array to the JSON tree. for the text, a comma should be
+ * taken out.
+ *
+ * @param nodeType the type of the child
+ */
+ void addNodeBottomHalf(JsonNodeType nodeType);
+
+ /**
+ * Returns the JSON tree after build operations in the format of string.
+ *
+ * @return the final string JSON tree after build operations
+ */
+ String getTreeString();
+
+ /**
+ * Returns the JSON tree after build operations in the format of string.
+ *
+ * @return the final ObjectNode JSON tree after build operations
+ */
+ ObjectNode getTreeNode();
+}
diff --git a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/JsonSerializer.java b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/JsonSerializer.java
new file mode 100644
index 0000000..a7c9197
--- /dev/null
+++ b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/JsonSerializer.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.yang.serializers.json;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.commons.io.IOUtils;
+import org.onosproject.yang.model.DataNode;
+import org.onosproject.yang.model.ResourceId;
+import org.onosproject.yang.runtime.CompositeData;
+import org.onosproject.yang.runtime.CompositeStream;
+import org.onosproject.yang.runtime.DefaultCompositeData;
+import org.onosproject.yang.runtime.DefaultCompositeStream;
+import org.onosproject.yang.runtime.DefaultResourceData;
+import org.onosproject.yang.runtime.ResourceData;
+import org.onosproject.yang.runtime.YangSerializer;
+import org.onosproject.yang.runtime.YangSerializerContext;
+import org.slf4j.Logger;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.yang.serializers.json.DecoderUtils.convertJsonToDataNode;
+import static org.onosproject.yang.serializers.json.DecoderUtils.convertUriToRid;
+import static org.onosproject.yang.serializers.json.EncoderUtils.convertDataNodeToJson;
+import static org.onosproject.yang.serializers.json.EncoderUtils.convertRidToUri;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the JSON serializer.
+ */
+public class JsonSerializer implements YangSerializer {
+ private static final String JSON_FORMAT = "JSON";
+
+ private final Logger log = getLogger(getClass());
+ private final ObjectMapper mapper = new ObjectMapper();
+
+ public ObjectMapper mapper() {
+ return mapper;
+ }
+
+ @Override
+ public String supportsFormat() {
+ return JSON_FORMAT;
+ }
+
+ @Override
+ public CompositeData decode(CompositeStream compositeStream,
+ YangSerializerContext yangSerializerContext) {
+ try {
+ ResourceId rid = convertUriToRid(compositeStream.resourceId(),
+ yangSerializerContext);
+
+ ObjectNode rootNode = null;
+
+ if (compositeStream.resourceData() != null) {
+ rootNode = (ObjectNode) mapper().
+ readTree(compositeStream.resourceData());
+ }
+
+ DataNode dataNode;
+ /*
+ * initializeDataNode by passing yangSerializerContext is
+ * intended to be used in a scenario wherein URL is NULL.
+ * initializeDataNode by passing resourceIdBuilder is
+ * intended to be used in a scenario when URL is not NULL
+ * and in this case the resourceId builder which was constructed
+ * for a URL, needs to be given as an Input parameter.
+ */
+ if (rid != null) {
+ dataNode = convertJsonToDataNode(rootNode,
+ new ResourceId.Builder(rid));
+
+ } else {
+ dataNode = convertJsonToDataNode(rootNode,
+ yangSerializerContext);
+ }
+
+ ResourceData resourceData = DefaultResourceData.builder().
+ addDataNode(dataNode).resourceId(rid).build();
+ return DefaultCompositeData.builder().resourceData(resourceData).build();
+ } catch (CloneNotSupportedException e) {
+ log.error("ERROR: JsonProcessingException {}",
+ e.getMessage());
+ log.debug("Exception in decode:", e);
+ throw new SerializerException("JSON serializer decode failure");
+ } catch (JsonProcessingException e) {
+ log.error("ERROR: JsonProcessingException {}",
+ e.getMessage());
+ log.debug("Exception in decode:", e);
+ throw new SerializerException("JSON serializer decode failure");
+ } catch (IOException ex) {
+ log.error("ERROR: decode ", ex);
+ throw new SerializerException("JSON serializer decode failure");
+ }
+ }
+
+
+ @Override
+ public CompositeStream encode(CompositeData compositeData,
+ YangSerializerContext yangSerializerContext) {
+ checkNotNull(compositeData, "compositeData cannot be null");
+
+ String uriString = convertRidToUri(compositeData.resourceData().
+ resourceId());
+ InputStream inputStream = null;
+ ObjectNode rootNode = null;
+
+ if (compositeData.resourceData().dataNodes() != null) {
+ rootNode = convertDataNodeToJson(compositeData.
+ resourceData().dataNodes().get(0));
+ }
+
+ if (rootNode != null) {
+ inputStream = IOUtils.toInputStream(rootNode.toString());
+ }
+
+ CompositeStream compositeStream = new DefaultCompositeStream(uriString,
+ inputStream);
+
+ return compositeStream;
+ }
+}
diff --git a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/JsonWalker.java b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/JsonWalker.java
new file mode 100644
index 0000000..aa3e935
--- /dev/null
+++ b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/JsonWalker.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.yang.serializers.json;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * Abstraction of an entity which provides interfaces for Json walk. This interface serve as common tools
+ * for anyone who needs to parse the json node with depth-first algorithm.
+ */
+public interface JsonWalker {
+
+ /**
+ * Walks the JSON data tree. Protocols implements JSON listener service
+ * and walks JSON tree with input as implemented object. JSON walker provides
+ * call backs to implemented methods. For the original json node(come from NB),
+ * there is a field name which is something like the module name of a YANG model.
+ * If not, the fieldName can be null.
+ *
+ * @param fieldName the original object node field
+ * @param jsonNode the json node which needs to be walk
+ */
+ void walkJsonNode(String fieldName, JsonNode jsonNode);
+}
diff --git a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/SerializerException.java b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/SerializerException.java
new file mode 100644
index 0000000..b7d899d
--- /dev/null
+++ b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/SerializerException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.yang.serializers.json;
+
+/**
+ * Represents class of errors related to JSON parse utils.
+ */
+public class SerializerException extends RuntimeException {
+
+ /**
+ * Constructs an exception with the specified message.
+ *
+ * @param message the message describing the specific nature of the error
+ */
+ public SerializerException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs an exception with the specified message and the underlying
+ * cause.
+ *
+ * @param message the message describing the specific nature of the error
+ * @param cause the underlying cause of this error
+ */
+ public SerializerException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/serializers/json/src/main/java/org/onosproject/yang/serializers/json/package-info.java b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/package-info.java
new file mode 100644
index 0000000..56ecc6a
--- /dev/null
+++ b/serializers/json/src/main/java/org/onosproject/yang/serializers/json/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * The JSON codec implementation of the YangSerializer interface.
+ */
+package org.onosproject.yang.serializers.json;
\ No newline at end of file
diff --git a/serializers/pom.xml b/serializers/pom.xml
new file mode 100644
index 0000000..6846261
--- /dev/null
+++ b/serializers/pom.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2017-present Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-yang-tools</artifactId>
+ <version>1.12-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-yang-serializers</artifactId>
+ <packaging>pom</packaging>
+
+ <modules>
+ <module>json</module>
+ </modules>
+
+ <description>Serializers for various codecs</description>
+</project>