diff --git a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/JsonToYdtListener.java b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/JsonToYdtListener.java index 7c59e1122d..11181167f3 100755 --- a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/JsonToYdtListener.java +++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/JsonToYdtListener.java @@ -110,21 +110,26 @@ public class JsonToYdtListener implements JsonListener { @Override public void exitJsonNode(JsonNode jsonNode) { - if (jsonNode.getNodeType() == ARRAY && nameStack.empty()) { + + if (isListArray) { + isListArray = false; + ydtBuilder.traverseToParent(); return; } - if (jsonNode.getNodeType() == ARRAY && !isListArray) { + if (jsonNode.getNodeType() == ARRAY) { + //check empty before pop + if (nameStack.empty()) { + return; + } nameStack.pop(); + //check empty after pop if (nameStack.empty()) { return; } defaultMultiInsNode = nameStack.get(nameStack.size() - 1); return; } - if (isListArray) { - isListArray = false; - } ydtBuilder.traverseToParent(); } diff --git a/protocols/restconf/server/utils/src/test/java/org/onosproject/protocol/restconf/server/utils/parser/api/TestYangSchemaId.java b/protocols/restconf/server/utils/src/test/java/org/onosproject/protocol/restconf/server/utils/parser/api/TestYangSchemaId.java new file mode 100644 index 0000000000..fb04710858 --- /dev/null +++ b/protocols/restconf/server/utils/src/test/java/org/onosproject/protocol/restconf/server/utils/parser/api/TestYangSchemaId.java @@ -0,0 +1,82 @@ +/* + * Copyright 2016-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.protocol.restconf.server.utils.parser.api; + +import java.util.Objects; + +/** + * A test class which represents YANG data node identifier which is a + * combination of name and namespace. + */ +public class TestYangSchemaId { + private String name; + private String namespace; + + /** + * Returns the name of the node. + * + * @return name of the node + */ + public String getName() { + return this.name; + } + + /** + * Sets name of the node. + * + * @param name name of the node + */ + public void setName(String name) { + this.name = name; + } + + /** + * Returns namespace of the node. + * + * @return namespace of the node + */ + public String getNameSpace() { + return this.namespace; + } + + /** + * Sets namespace of the node. + * + * @param namespace namespace of the node + */ + public void setNameSpace(String namespace) { + this.namespace = namespace; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (!(obj instanceof TestYangSchemaId)) { + return false; + } else { + TestYangSchemaId other = (TestYangSchemaId) obj; + return Objects.equals(this.name, other.name) && + Objects.equals(this.namespace, other.namespace); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.name, this.namespace); + } +} diff --git a/protocols/restconf/server/utils/src/test/java/org/onosproject/protocol/restconf/server/utils/parser/api/TestYdtBuilder.java b/protocols/restconf/server/utils/src/test/java/org/onosproject/protocol/restconf/server/utils/parser/api/TestYdtBuilder.java new file mode 100644 index 0000000000..c411ebeca4 --- /dev/null +++ b/protocols/restconf/server/utils/src/test/java/org/onosproject/protocol/restconf/server/utils/parser/api/TestYdtBuilder.java @@ -0,0 +1,182 @@ +/* + * Copyright 2016-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.protocol.restconf.server.utils.parser.api; + +import org.onosproject.yms.ydt.YdtBuilder; +import org.onosproject.yms.ydt.YdtContext; +import org.onosproject.yms.ydt.YdtContextOperationType; +import org.onosproject.yms.ydt.YdtType; +import org.onosproject.yms.ydt.YmsOperationType; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.onosproject.yms.ydt.YdtContextOperationType.NONE; +import static org.onosproject.yms.ydt.YdtType.*; + +/** + * A test class which represents YANG request work bench which contains all + * parameters for request handling and methods to build and obtain YANG data + * tree which is data (sub)instance representation, abstract of protocol. + */ +public class TestYdtBuilder implements YdtBuilder { + + private TestYdtNode curNode; + + private TestYdtNode rootNode; + + private final YmsOperationType ymsOperationType; + + private YdtContextOperationType ydtDefaultOpType; + + /** + * Creates an instance of YANG request work bench which is use to initialize + * logical rootNode and and schema registry. + * + * @param name name of logical container of a protocol + * which is a holder of the complete tree + * @param namespace namespace of logical container + * @param opType type of operation done by using YANG + * interface + */ + public TestYdtBuilder(String name, String namespace, + YmsOperationType opType) { + TestYangSchemaId nodeId = new TestYangSchemaId(); + nodeId.setName(name); + nodeId.setNameSpace(namespace); + setRootNode(new TestYdtNode(nodeId, SINGLE_INSTANCE_NODE)); + ymsOperationType = opType; + } + + private void setRootNode(TestYdtNode node) { + rootNode = node; + curNode = node; + } + + @Override + public YdtContext getRootNode() { + return rootNode; + } + + @Override + public YmsOperationType getYmsOperationType() { + return ymsOperationType; + } + + @Override + public void setRootTagAttributeMap(Map attributeTag) { + + } + + @Override + public Map getRootTagAttributeMap() { + return null; + } + + @Override + public void addChild(String name, String namespace) { + addChild(name, namespace, SINGLE_INSTANCE_NODE, NONE); + } + + @Override + public void addChild(String name, String namespace, YdtType ydtType) { + addChild(name, namespace, ydtType, NONE); + + } + + @Override + public void addChild(String name, String namespace, + YdtContextOperationType opType) { + addChild(name, namespace, SINGLE_INSTANCE_NODE, opType); + } + + @Override + public void addChild(String name, String namespace, YdtType ydtType, + YdtContextOperationType opType) { + TestYangSchemaId id = new TestYangSchemaId(); + id.setName(name); + String ns = namespace != null ? namespace : + curNode.getYdtNodeIdentifier().getNameSpace(); + id.setNameSpace(ns); + TestYdtNode childNode = new TestYdtNode(id, ydtType); + + YdtContextOperationType type = opType == null ? NONE : opType; + childNode.setYdtContextOperationType(type); + + curNode.addChild(childNode); + + // Updating the curNode. + curNode = childNode; + } + + @Override + public void addLeaf(String name, String namespace, String value) { + addLeaf(name, namespace, value, null, SINGLE_INSTANCE_LEAF_VALUE_NODE); + } + + @Override + public void addLeaf(String name, String namespace, Set valueSet) { + addLeaf(name, namespace, null, valueSet, MULTI_INSTANCE_LEAF_VALUE_NODE); + } + + @Override + public void addMultiInstanceChild(String name, String namespace, + List valueList) { + addChild(name, namespace, MULTI_INSTANCE_LEAF_VALUE_NODE, NONE); + if (curNode.getYdtType() == MULTI_INSTANCE_LEAF_VALUE_NODE) { + for (String value : valueList) { + curNode.addValue(value); + } + } + } + + private void addLeaf(String name, String namespace, String value, + Set valueSet, YdtType ydtType) { + addChild(name, namespace, ydtType, NONE); + + if (value != null) { + curNode.addValue(value); + } else if (valueSet != null) { + curNode.addValueSet(valueSet); + } + } + + @Override + public void traverseToParent() { + curNode = curNode.getParent(); + } + + @Override + public YdtContext getCurNode() { + return curNode; + } + + @Override + public void setDefaultEditOperationType(YdtContextOperationType opType) { + ydtDefaultOpType = opType; + } + + /** + * Returns the default context operation type of a YDT builder. + * + * @return default context operation type + */ + public YdtContextOperationType getYdtDefaultOpType() { + return ydtDefaultOpType; + } +} diff --git a/protocols/restconf/server/utils/src/test/java/org/onosproject/protocol/restconf/server/utils/parser/api/TestYdtNode.java b/protocols/restconf/server/utils/src/test/java/org/onosproject/protocol/restconf/server/utils/parser/api/TestYdtNode.java new file mode 100644 index 0000000000..4e609a11fb --- /dev/null +++ b/protocols/restconf/server/utils/src/test/java/org/onosproject/protocol/restconf/server/utils/parser/api/TestYdtNode.java @@ -0,0 +1,232 @@ +/* + * Copyright 2016-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.protocol.restconf.server.utils.parser.api; + +import org.onosproject.yms.ydt.YdtContext; +import org.onosproject.yms.ydt.YdtContextOperationType; +import org.onosproject.yms.ydt.YdtExtendedInfoType; +import org.onosproject.yms.ydt.YdtType; + +import java.util.HashSet; +import java.util.Set; + +/** + * A test class which represents a general instance node in YANG data tree. + */ +public class TestYdtNode implements YdtContext { + + private TestYdtNode parent; + private TestYdtNode child; + private TestYdtNode nextSibling; + private TestYdtNode previousSibling; + private TestYdtNode lastChild; + private YdtType ydtType; + private String value; + private final Set valueSet = new HashSet<>(); + private TestYangSchemaId id; + private YdtContextOperationType ydtContextOperationType; + + /** + * Creates a general YANG instance node object. + * + * @param id node identifier of YDT multi instance node . + * @param ydtType type of YDT node to be added + */ + public TestYdtNode(TestYangSchemaId id, YdtType ydtType) { + this.id = id; + this.ydtType = ydtType; + } + + @Override + public String getName() { + return id.getName(); + } + + @Override + public String getNamespace() { + return id.getNameSpace(); + } + + @Override + public T getYdtContextExtendedInfo() { + return null; + } + + @Override + public YdtExtendedInfoType getYdtExtendedInfoType() { + return null; + } + + @Override + public YdtType getYdtType() { + return ydtType; + } + + @Override + public TestYdtNode getParent() { + return parent; + } + + @Override + public TestYdtNode getFirstChild() { + return child; + } + + @Override + public TestYdtNode getLastChild() { + return lastChild; + } + + @Override + public TestYdtNode getNextSibling() { + return nextSibling; + } + + @Override + public YdtContext getPreviousSibling() { + return previousSibling; + } + + @Override + public String getValue() { + return value; + } + + @Override + public Set getValueSet() { + return valueSet; + } + + /** + * Sets the parent of node. + * + * @param parent node + */ + public void setParent(TestYdtNode parent) { + this.parent = parent; + } + + /** + * Sets the first instance of a child node. + * + * @param child is only child to be set + */ + public void setChild(TestYdtNode child) { + this.child = child; + } + + /** + * Sets the last instance of a child node. + * + * @param child is last child to be set + */ + public void setLastChild(TestYdtNode child) { + lastChild = child; + } + + /** + * Sets the next sibling of node. + * + * @param sibling YANG node + */ + public void setNextSibling(TestYdtNode sibling) { + nextSibling = sibling; + } + + /** + * Sets the previous sibling. + * + * @param previousSibling points to predecessor sibling + */ + public void setPreviousSibling(TestYdtNode previousSibling) { + this.previousSibling = previousSibling; + } + + /** + * Returns object node identifier. + * + * @return node identifier + */ + public TestYangSchemaId getYdtNodeIdentifier() { + return id; + } + + /** + * Adds a child node. + * The children sibling list will be sorted based on node + * type. This will add single child or sub-tree based on isAtomic flag. + * + * @param newChild refers to a new child to be added + */ + public void addChild(YdtContext newChild) { + TestYdtNode node = (TestYdtNode) newChild; + + if (node.getParent() == null) { + node.setParent(this); + } + + // If new node needs to be added as first child. + if (getFirstChild() == null) { + setChild(node); + setLastChild(node); + return; + } + + // If new node needs to be added as last child. + TestYdtNode curNode = getLastChild(); + curNode.setNextSibling(node); + node.setPreviousSibling(curNode); + setLastChild(node); + } + + /** + * Adds the given value to the non single instance leaf node. + * + * @param value value in a single instance node + */ + public void addValue(String value) { + this.value = value; + } + + /** + * Adds the given valueSet to the non multi instance leaf node. + * + * @param values value set in a multi instance leaf node + */ + public void addValueSet(Set values) { + valueSet.addAll(values); + } + + + /** + * Sets the context operation type for the YDT node. + * + * @param opType context operation type + */ + public void setYdtContextOperationType(YdtContextOperationType opType) { + this.ydtContextOperationType = opType; + } + + /** + * Returns the context operation type for the YDT node. + * + * @return context operation type + */ + public YdtContextOperationType getYdtContextOperationType() { + return ydtContextOperationType; + } +} diff --git a/protocols/restconf/server/utils/src/test/java/org/onosproject/restconf/utils/parser/json/DefaultJsonWalkerTest.java b/protocols/restconf/server/utils/src/test/java/org/onosproject/protocol/restconf/server/utils/parser/json/DefaultJsonWalkerTest.java similarity index 97% rename from protocols/restconf/server/utils/src/test/java/org/onosproject/restconf/utils/parser/json/DefaultJsonWalkerTest.java rename to protocols/restconf/server/utils/src/test/java/org/onosproject/protocol/restconf/server/utils/parser/json/DefaultJsonWalkerTest.java index c82f0f8d8c..0c4f9b0b9e 100644 --- a/protocols/restconf/server/utils/src/test/java/org/onosproject/restconf/utils/parser/json/DefaultJsonWalkerTest.java +++ b/protocols/restconf/server/utils/src/test/java/org/onosproject/protocol/restconf/server/utils/parser/json/DefaultJsonWalkerTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.onosproject.restconf.utils.parser.json; +package org.onosproject.protocol.restconf.server.utils.parser.json; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -22,7 +22,6 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.Before; import org.junit.Test; import org.onosproject.protocol.restconf.server.utils.parser.api.JsonListener; -import org.onosproject.protocol.restconf.server.utils.parser.json.DefaultJsonWalker; import java.io.InputStream; import java.util.ArrayList; diff --git a/protocols/restconf/server/utils/src/test/java/org/onosproject/protocol/restconf/server/utils/parser/json/JsonToYdtListenerTest.java b/protocols/restconf/server/utils/src/test/java/org/onosproject/protocol/restconf/server/utils/parser/json/JsonToYdtListenerTest.java new file mode 100644 index 0000000000..07cc223d13 --- /dev/null +++ b/protocols/restconf/server/utils/src/test/java/org/onosproject/protocol/restconf/server/utils/parser/json/JsonToYdtListenerTest.java @@ -0,0 +1,136 @@ +/* + * Copyright 2016-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.protocol.restconf.server.utils.parser.json; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.ImmutableSet; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.protocol.restconf.server.utils.parser.api.JsonWalker; +import org.onosproject.protocol.restconf.server.utils.parser.api.TestYdtBuilder; +import org.onosproject.yms.ydt.YdtBuilder; +import org.onosproject.yms.ydt.YdtContext; + +import java.io.InputStream; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.onosproject.yms.ydt.YdtType.*; +import static org.onosproject.yms.ydt.YmsOperationType.QUERY_REQUEST; + +/** + * Unit tests for JsonToYtdListener. + */ +public class JsonToYdtListenerTest { + private static final String RESTCONF_ROOT = "/onos/restconf"; + private static final String WRONG_STRUCTURE = "The Ydt structure is wrong!"; + private static final String WRONG_TYPE = "The Ydt type is wrong!"; + + private YdtBuilder builder; + private JsonToYdtListener listener; + private JsonWalker jsonWalker; + + @Before + public void setup() throws Exception { + builder = new TestYdtBuilder(RESTCONF_ROOT, null, QUERY_REQUEST); + listener = new JsonToYdtListener(builder); + jsonWalker = new DefaultJsonWalker(); + } + + private ObjectNode loadJsonFile(String path) throws Exception { + InputStream jsonStream = getClass().getResourceAsStream(path); + ObjectMapper mapper = new ObjectMapper(); + return (ObjectNode) mapper.readTree(jsonStream); + } + + @Test + public void testArrayNodeTransfer() throws Exception { + ObjectNode arrayNode = loadJsonFile("/arrayNode.json"); + jsonWalker.walk(listener, null, arrayNode); + YdtContext rootNode = builder.getRootNode(); + YdtContext firstChild = rootNode.getFirstChild(); + YdtContext nextSibling = firstChild.getNextSibling(); + assertEquals(WRONG_TYPE, SINGLE_INSTANCE_LEAF_VALUE_NODE, + firstChild.getYdtType()); + assertEquals(WRONG_STRUCTURE, "surname", firstChild.getName()); + assertEquals(WRONG_TYPE, MULTI_INSTANCE_NODE, nextSibling.getYdtType()); + assertEquals(WRONG_STRUCTURE, "networklist", nextSibling.getName()); + assertEquals(WRONG_STRUCTURE, "networklist", + nextSibling.getNextSibling().getName()); + assertEquals(WRONG_STRUCTURE, "networklist", + rootNode.getLastChild().getName()); + } + + @Test + public void testListInListNodeTransfer() throws Exception { + ObjectNode arrayNode = loadJsonFile("/listInList.json"); + jsonWalker.walk(listener, null, arrayNode); + YdtContext rootNode = builder.getRootNode(); + YdtContext firstChild = rootNode.getFirstChild(); + YdtContext levelOneArray = firstChild.getFirstChild(); + assertEquals(WRONG_STRUCTURE, "container-identifier1", + firstChild.getName()); + assertEquals(WRONG_STRUCTURE, "list-identifier2", + levelOneArray.getName()); + assertEquals(WRONG_STRUCTURE, "list-identifier2", + levelOneArray.getNextSibling().getName()); + + YdtContext identifier3Node = levelOneArray.getLastChild(); + assertEquals(WRONG_STRUCTURE, "container-identifier3", + identifier3Node.getName()); + YdtContext identifier4Node = identifier3Node.getLastChild(); + assertEquals(WRONG_STRUCTURE, "list-identifier4", + identifier4Node.getName()); + YdtContext identifier5ListNode = identifier4Node.getLastChild(); + assertEquals(WRONG_STRUCTURE, "leaf-list-identifier5", + identifier5ListNode.getName()); + assertEquals(WRONG_TYPE, MULTI_INSTANCE_LEAF_VALUE_NODE, + identifier5ListNode.getYdtType()); + } + + @Test + public void testListAfterLeafList() throws Exception { + ObjectNode arrayNode = loadJsonFile("/listAfterLeaflist.json"); + jsonWalker.walk(listener, null, arrayNode); + YdtContext rootNode = builder.getRootNode(); + YdtContext firstChild = rootNode.getFirstChild(); + YdtContext second = firstChild.getNextSibling(); + YdtContext lastChild = rootNode.getLastChild(); + assertEquals(WRONG_STRUCTURE, "leaf-identifier1", firstChild.getName()); + assertEquals(WRONG_STRUCTURE, "leaf-list-identifier1", second.getName()); + assertEquals(WRONG_STRUCTURE, "list-identifier1", lastChild.getName()); + } + + @Test + public void testLeafListAfterList() throws Exception { + ObjectNode arrayNode = loadJsonFile("/leaflistAfterlist.json"); + jsonWalker.walk(listener, null, arrayNode); + YdtContext rootNode = builder.getRootNode(); + YdtContext firstChild = rootNode.getFirstChild(); + YdtContext second = firstChild.getNextSibling(); + YdtContext lastChild = rootNode.getLastChild(); + assertEquals(WRONG_STRUCTURE, "leaf-identifier1", firstChild.getName()); + assertEquals(WRONG_STRUCTURE, "list-identifier1", second.getName()); + YdtContext level2Child = second.getFirstChild(); + assertEquals(WRONG_STRUCTURE, "leaf-identifier2", level2Child.getName()); + assertEquals(WRONG_STRUCTURE, "5", level2Child.getValue()); + assertEquals(WRONG_STRUCTURE, "leaf-list-identifier1", lastChild.getName()); + Set sets = ImmutableSet.of("5", "12"); + assertEquals(WRONG_STRUCTURE, sets, lastChild.getValueSet()); + } +} \ No newline at end of file diff --git a/protocols/restconf/server/utils/src/test/resources/leaflistAfterlist.json b/protocols/restconf/server/utils/src/test/resources/leaflistAfterlist.json new file mode 100644 index 0000000000..8b0c4e178b --- /dev/null +++ b/protocols/restconf/server/utils/src/test/resources/leaflistAfterlist.json @@ -0,0 +1,12 @@ +{ + "leaf-identifier1": "1", + "list-identifier1": [ + { + "leaf-identifier2": "5" + } + ], + "leaf-list-identifier1": [ + "5", + "12" + ] +} \ No newline at end of file diff --git a/protocols/restconf/server/utils/src/test/resources/listAfterLeaflist.json b/protocols/restconf/server/utils/src/test/resources/listAfterLeaflist.json new file mode 100644 index 0000000000..bbe25e1a25 --- /dev/null +++ b/protocols/restconf/server/utils/src/test/resources/listAfterLeaflist.json @@ -0,0 +1,12 @@ +{ + "leaf-identifier1": "1", + "leaf-list-identifier1": [ + "5", + "12" + ], + "list-identifier1": [ + { + "leaf-identifier2": "5" + } + ] +} \ No newline at end of file diff --git a/protocols/restconf/server/utils/src/test/resources/listInList.json b/protocols/restconf/server/utils/src/test/resources/listInList.json new file mode 100644 index 0000000000..185d878322 --- /dev/null +++ b/protocols/restconf/server/utils/src/test/resources/listInList.json @@ -0,0 +1,39 @@ +{ + "container-identifier1": { + "list-identifier2": [ + { + "leaf-identifier3": "enum1", + "container-identifier3": { + "leaf-identifier4": "", + "leaf-list-identifier4": [ + "type-pattern-string6" + ], + "list-identifier4": [ + { + "leaf-identifier5": "type-pattern-string7", + "leaf-list-identifier5": [ + "type-pattern-string7", + "type-pattern-string7", + "type-pattern-string7" + ] + } + ] + } + }, + { + "leaf-identifier3": "enum2", + "list-identifier3": [ + { + "leaf-identifier4": "myidentity", + "container-identifier4": { + "leaf-identifier5": "type-pattern-string3", + "leaf-list-identifier5": [ + "type-pattern-string3" + ] + } + } + ] + } + ] + } +} \ No newline at end of file