When a YANG model contains an anyxml element the output does not render correctly
in JSON in Restconf. XML output works however. Upon investigation we found issue
was with recognising and handling collections in XML. This patch addresses the
same and also enhanced and added more unit tests.
Change-Id: Icb238dd95a3b9af9c4d0f48ff673a7c285925364
Signed-off-by: Balaji Varadaraju <bvaradar@luminanetworks.com>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
package org.opendaylight.yangtools.yang.data.codec.gson;
import static java.util.Objects.requireNonNull;
-import static org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream.ANYXML_ARRAY_ELEMENT_ID;
import static org.w3c.dom.Node.ELEMENT_NODE;
import static org.w3c.dom.Node.TEXT_NODE;
}
private void writeXmlNode(final Node node) throws IOException {
+ if (isArrayElement(node)) {
+ writeArrayContent(node);
+ return;
+ }
final Element firstChildElement = getFirstChildElement(node);
if (firstChildElement == null) {
writeXmlValue(node);
- } else if (ANYXML_ARRAY_ELEMENT_ID.equals(firstChildElement.getNodeName())) {
- writer.beginArray();
- writeArray(firstChildElement);
- writer.endArray();
} else {
- writer.beginObject();
- writeObject(firstChildElement);
- writer.endObject();
+ writeObjectContent(firstChildElement);
}
}
- private void writeArray(Node node) throws IOException {
- while (node != null) {
- if (ELEMENT_NODE == node.getNodeType()) {
- writeXmlNode(node);
+ private void writeArrayContent(final Node node) throws IOException {
+ writer.beginArray();
+ handleArray(node);
+ writer.endArray();
+ }
+
+ private void writeObjectContent(final Element firstChildElement) throws IOException {
+ writer.beginObject();
+ writeObject(firstChildElement);
+ writer.endObject();
+ }
+
+ private static boolean isArrayElement(final Node node) {
+ if (ELEMENT_NODE == node.getNodeType()) {
+ final String nodeName = node.getNodeName();
+ for (Node nextNode = node.getNextSibling(); nextNode != null; nextNode = nextNode.getNextSibling()) {
+ if (ELEMENT_NODE == nextNode.getNodeType() && nodeName.equals(nextNode.getNodeName())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private void handleArray(final Node node) throws IOException {
+ final Element parentNode = (Element)node.getParentNode();
+ final NodeList elementsList = parentNode.getElementsByTagName(node.getNodeName());
+ for (int i = 0, length = elementsList.getLength(); i < length; i++) {
+ final Node arrayElement = elementsList.item(i);
+ final Element parent = (Element)arrayElement.getParentNode();
+ if (parentNode.isSameNode(parent)) {
+ final Element firstChildElement = getFirstChildElement(arrayElement);
+ if (firstChildElement != null) {
+ writeObjectContent(firstChildElement);
+ } else {
+ // It may be scalar
+ writeXmlValue(arrayElement);
+ }
}
- node = node.getNextSibling();
}
}
private void writeObject(Node node) throws IOException {
+ String previousNodeName = "";
while (node != null) {
if (ELEMENT_NODE == node.getNodeType()) {
- writer.name(node.getNodeName());
- writeXmlNode(node);
+ if (!node.getNodeName().equals(previousNodeName)) {
+ previousNodeName = node.getNodeName();
+ writer.name(node.getNodeName());
+ writeXmlNode(node);
+ }
}
node = node.getNextSibling();
}
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-
package org.opendaylight.yangtools.yang.data.codec.gson;
import static org.junit.Assert.assertEquals;
final DOMSource Lf12AnyActualValue = getParsedAnyXmlValue(transformedInput, LF12_ANY);
final DOMSource Lf12AnyExpectedValue = createLf12AnyXmlCompositeValue("ns:complex:json", "lf12-any");
verifyTransformedAnyXmlNodeValue(Lf12AnyExpectedValue, Lf12AnyActualValue);
-
// lf13-any check
final DOMSource Lf13AnyActualValue = getParsedAnyXmlValue(transformedInput, LF13_ANY);
final DOMSource Lf13AnyExpectedValue = createLf13AnyXmlCompositeValue("ns:complex:json", "lf13-any");
SchemaPath.ROOT);
final JsonParser parser = new JsonParser();
- final JsonElement expected = parser.parse(inputJson);
+ final String expectedJson = loadTextFile("/bug8927/json/composite.json");
+ final JsonElement expected = parser.parse(expectedJson);
final JsonElement actual = parser.parse(serializationResult);
assertTrue(expected.equals(actual));
}
@Test
- public void bug8927Test() throws Exception {
+ public void bug8927TestComplexArrayWithOthers() throws Exception {
+ executebug8927Test("/bug8927/xml/complex_array_with_other_elements.xml",
+ "/bug8927/json/complex_array_with_other_elements.json");
+ }
+
+ @Test
+ public void bug8927TestComplexArray() throws Exception {
+ executebug8927Test("/bug8927/xml/complex_array.xml", "/bug8927/json/complex_array.json");
+ }
+
+ @Test
+ public void bug8927TestScalarArrayWithOthers() throws Exception {
+ executebug8927Test("/bug8927/xml/scalar_array_with_other_elements.xml",
+ "/bug8927/json/scalar_array_with_other_elements.json");
+ }
+
+ @Test
+ public void bug8927TestScalarArray() throws Exception {
+ executebug8927Test("/bug8927/xml/scalar_array.xml", "/bug8927/json/scalar_array.json");
+ }
+
+ private void executebug8927Test(final String inputXmlFile, final String expectedJsonFile) throws Exception {
final InputStream resourceAsStream = YangModeledAnyXmlSupportTest.class
- .getResourceAsStream("/bug8927/xml/input.xml");
+ .getResourceAsStream(inputXmlFile);
final NormalizedNodeResult result = new NormalizedNodeResult();
loadXmlToNormalizedNodes(resourceAsStream, result, schemaContext);
.getChild(new NodeIdentifier(QName.create("bug8927.test", "2017-01-01", "foo")));
assertTrue(data.isPresent());
final String jsonOutput = normalizedNodesToJsonString(data.get(), schemaContext, SchemaPath.ROOT);
-
final JsonParser parser = new JsonParser();
final JsonElement expextedJson = parser
- .parse(new FileReader(new File(getClass().getResource("/bug8927/json/expected.json").toURI())));
+ .parse(new FileReader(new File(getClass().getResource(expectedJsonFile).toURI())));
final JsonElement serializedJson = parser.parse(jsonOutput);
-
assertEquals(expextedJson, serializedJson);
}
--- /dev/null
+{
+ "bug8927:foo": {
+ "alarm-information": {
+ "alarm-detail": [{
+ "alarm-time": "2017-08-02 19:03:44 UTC",
+ "alarm-class": "Minor",
+ "alarm-description": [{
+ "test": 123
+ }, {
+ "test": 456
+ }],
+ "alarm-short-description": "gnf-creation",
+ "alarm-type": "License"
+ }, {
+ "alarm-time": "2017-07-25 16:04:31 UTC",
+ "alarm-class": ["Minor", "Minor1"],
+ "alarm-description": "VMHost RE 0 host application failed",
+ "alarm-short-description": "VMHost RE 0 host ap",
+ "alarm-type": "Chassis"
+ }]
+ }
+ }
+}
--- /dev/null
+{
+ "bug8927:foo": {
+ "alarm-information": {
+ "alarm-summary": {
+ "active-alarm-count": ""
+ },
+ "alarm-detail":[ {
+ "alarm-time": "2017-08-02 19:03:44 UTC",
+ "alarm-class": "Minor",
+ "alarm-description": "desc",
+ "alarm-short-description": "gnf-creation",
+ "alarm-type": "License"
+ },
+ {
+ "alarm-time": "2017-07-25 16:04:31 UTC",
+ "alarm-class": "Minor",
+ "alarm-description": "VMHost RE 0 host application failed",
+ "alarm-short-description": "VMHost RE 0 host ap",
+ "alarm-type": "Chassis"
+ },
+ {
+ "alarm-time": "2017-07-25 16:04:31 UTC",
+ "alarm-class": "Major",
+ "alarm-description": "PDM 0 incompatible with chassis type",
+ "alarm-short-description": "PDM 0 incompatible",
+ "alarm-type": "Chassis"
+ },
+ {
+ "alarm-time": "2017-07-25 16:04:31 UTC",
+ "alarm-class": "Major",
+ "alarm-description": "PDM 1 incompatible with chassis type",
+ "alarm-short-description": "PDM 1 incompatible",
+ "alarm-type": "Chassis"
+ },
+ {
+ "alarm-time": "2017-07-25 16:04:31 UTC",
+ "alarm-class": "Major",
+ "alarm-description": "PDM 2 incompatible with chassis type",
+ "alarm-short-description": "PDM 2 incompatible",
+ "alarm-type": "Chassis"
+ },
+ {
+ "alarm-time": "2017-07-25 16:04:31 UTC",
+ "alarm-class": "Major",
+ "alarm-description": "PDM 3 incompatible with chassis type",
+ "alarm-short-description": "PDM 3 incompatible",
+ "alarm-type": "Chassis"
+ },
+ {
+ "alarm-time": "2017-07-25 16:04:31 UTC",
+ "alarm-class": "Major",
+ "alarm-description": "No Input Feed Selected for PSM 1",
+ "alarm-short-description": "No Input Feed Selec",
+ "alarm-type": "Chassis"
+ },
+ {
+ "alarm-time": "2017-07-25 16:04:31 UTC",
+ "alarm-class": "Major",
+ "alarm-description": "No Input Feed Selected for PSM 2",
+ "alarm-short-description": "No Input Feed Selec",
+ "alarm-type": "Chassis"
+ },
+ {
+ "alarm-time": "2017-07-25 16:04:31 UTC",
+ "alarm-class": "Major",
+ "alarm-description": "No Input Feed Selected for PSM 3",
+ "alarm-short-description": "No Input Feed Selec",
+ "alarm-type": "Chassis"
+ },
+ {
+ "alarm-time": "2017-07-25 16:04:31 UTC",
+ "alarm-class": "Major",
+ "alarm-description": "No Input Feed Selected for PSM 4",
+ "alarm-short-description": "No Input Feed Selec",
+ "alarm-type": "Chassis"
+ },
+ {
+ "alarm-time": "2017-07-25 16:04:31 UTC",
+ "alarm-class": "Major",
+ "alarm-description": "No Input Feed Selected for PSM 10",
+ "alarm-short-description": "No Input Feed Selec",
+ "alarm-type": "Chassis"
+ },
+ {
+ "alarm-time": "2017-07-25 16:04:31 UTC",
+ "alarm-class": "Major",
+ "alarm-description": "No Input Feed Selected for PSM 11",
+ "alarm-short-description": "No Input Feed Selec",
+ "alarm-type": "Chassis"
+ },
+ {
+ "alarm-time": "2017-07-25 16:04:31 UTC",
+ "alarm-class": "Major",
+ "alarm-description": "No Input Feed Selected for PSM 12",
+ "alarm-short-description": "No Input Feed Selec",
+ "alarm-type": "Chassis"
+ },
+ {
+ "alarm-time": "2017-07-25 16:04:31 UTC",
+ "alarm-class": "Major",
+ "alarm-description": "No Input Feed Selected for PSM 13",
+ "alarm-short-description": "No Input Feed Selec",
+ "alarm-type": "Chassis"
+ },
+ {
+ "alarm-time": "2017-07-25 16:04:31 UTC",
+ "alarm-class": "Minor",
+ "alarm-description": "VMHost RE 1 host application failed",
+ "alarm-short-description": "VMHost RE 1 host ap",
+ "alarm-type": "Chassis"
+ },
+ {
+ "alarm-time": "2017-07-25 16:04:31 UTC",
+ "alarm-class": "Minor",
+ "alarm-description": "Check plane 1 Fabric Chip",
+ "alarm-short-description": "Check plane 1 FCHIP",
+ "alarm-type": "Chassis"
+ }
+ ]
+ }
+ }
+}
--- /dev/null
+{
+ "complexjson:cont1": {
+ "lf14-any": {
+ "anyxml-object-a": {
+ "data-a1": 10,
+ "data-a2": 11
+ },
+ "anyxml-object-b": {
+ "child-object-b1": {
+ "data-b1": 5.5
+ },
+ "child-object-b2": {
+ "data-b2": "b2-val"
+ }
+ }
+ },
+ "lf12-any": {
+ "array-element": [true, {
+ "baz": {
+ "array-element": [120, "str-val", false]
+ }
+ }, {
+ "foo": null
+ }]
+ },
+ "lf13-any": {
+ "anyxml-array-a": {
+ "array-element": [{
+ "foo": true
+ }, 10, {
+ "bar": false
+ }]
+ },
+ "anyxml-array-b": {
+ "array-element": [1, 2, {
+ "array-element": [4, 5]
+ }, 7]
+ }
+ }
+ }
+}
--- /dev/null
+{
+ "bug8927:foo": {
+ "alarm-information": {
+ "alarm-summary": {
+ "active-alarm-count": ""
+ },
+ "elmt": [1, 2, 3]
+ }
+ }
+}
--- /dev/null
+{
+ "bug8927:foo": {
+ "alarm-information": {
+ "alarm-summary": {
+ "active-alarm-count": ""
+ },
+ "elmt": [1, 2, 3],
+ "test": 123
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+<foo xmlns="bug8927.test">
+ <alarm-information xmlns="bug8927.test">
+ <alarm-detail>
+ <alarm-time>
+ 2017-08-02 19:03:44 UTC
+ </alarm-time>
+ <alarm-class>Minor</alarm-class>
+ <alarm-description>
+ <test>123</test>
+ </alarm-description>
+ <alarm-description>
+ <test>456</test>
+ </alarm-description>
+ <alarm-short-description>gnf-creation</alarm-short-description>
+ <alarm-type>License</alarm-type>
+ </alarm-detail>
+ <alarm-detail>
+ <alarm-time>
+ 2017-07-25 16:04:31 UTC
+ </alarm-time>
+ <alarm-class>Minor</alarm-class>
+ <alarm-class>Minor1</alarm-class>
+ <alarm-description>VMHost RE 0 host application failed</alarm-description>
+ <alarm-short-description>VMHost RE 0 host ap</alarm-short-description>
+ <alarm-type>Chassis</alarm-type>
+ </alarm-detail>
+ </alarm-information>
+</foo>
+</root>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+<foo xmlns="bug8927.test">
+ <alarm-information xmlns="bug8927.test">
+ <alarm-summary>
+ <active-alarm-count/>
+ </alarm-summary>
+
+ <alarm-detail>
+ <alarm-time>
+ 2017-08-02 19:03:44 UTC
+ </alarm-time>
+ <alarm-class>Minor</alarm-class>
+ <alarm-description>desc</alarm-description>
+ <alarm-short-description>gnf-creation</alarm-short-description>
+ <alarm-type>License</alarm-type>
+ </alarm-detail>
+ <alarm-detail>
+ <alarm-time>
+ 2017-07-25 16:04:31 UTC
+ </alarm-time>
+ <alarm-class>Minor</alarm-class>
+ <alarm-description>VMHost RE 0 host application failed</alarm-description>
+ <alarm-short-description>VMHost RE 0 host ap</alarm-short-description>
+ <alarm-type>Chassis</alarm-type>
+ </alarm-detail>
+ <alarm-detail>
+ <alarm-time>
+ 2017-07-25 16:04:31 UTC
+ </alarm-time>
+ <alarm-class>Major</alarm-class>
+ <alarm-description>PDM 0 incompatible with chassis type</alarm-description>
+ <alarm-short-description>PDM 0 incompatible</alarm-short-description>
+ <alarm-type>Chassis</alarm-type>
+ </alarm-detail>
+ <alarm-detail>
+ <alarm-time>
+ 2017-07-25 16:04:31 UTC
+ </alarm-time>
+ <alarm-class>Major</alarm-class>
+ <alarm-description>PDM 1 incompatible with chassis type</alarm-description>
+ <alarm-short-description>PDM 1 incompatible</alarm-short-description>
+ <alarm-type>Chassis</alarm-type>
+ </alarm-detail>
+ <alarm-detail>
+ <alarm-time>
+ 2017-07-25 16:04:31 UTC
+ </alarm-time>
+ <alarm-class>Major</alarm-class>
+ <alarm-description>PDM 2 incompatible with chassis type</alarm-description>
+ <alarm-short-description>PDM 2 incompatible</alarm-short-description>
+ <alarm-type>Chassis</alarm-type>
+ </alarm-detail>
+ <alarm-detail>
+ <alarm-time>
+ 2017-07-25 16:04:31 UTC
+ </alarm-time>
+ <alarm-class>Major</alarm-class>
+ <alarm-description>PDM 3 incompatible with chassis type</alarm-description>
+ <alarm-short-description>PDM 3 incompatible</alarm-short-description>
+ <alarm-type>Chassis</alarm-type>
+ </alarm-detail>
+ <alarm-detail>
+ <alarm-time>
+ 2017-07-25 16:04:31 UTC
+ </alarm-time>
+ <alarm-class>Major</alarm-class>
+ <alarm-description>No Input Feed Selected for PSM 1</alarm-description>
+ <alarm-short-description>No Input Feed Selec</alarm-short-description>
+ <alarm-type>Chassis</alarm-type>
+ </alarm-detail>
+ <alarm-detail>
+ <alarm-time>
+ 2017-07-25 16:04:31 UTC
+ </alarm-time>
+ <alarm-class>Major</alarm-class>
+ <alarm-description>No Input Feed Selected for PSM 2</alarm-description>
+ <alarm-short-description>No Input Feed Selec</alarm-short-description>
+ <alarm-type>Chassis</alarm-type>
+ </alarm-detail>
+ <alarm-detail>
+ <alarm-time>
+ 2017-07-25 16:04:31 UTC
+ </alarm-time>
+ <alarm-class>Major</alarm-class>
+ <alarm-description>No Input Feed Selected for PSM 3</alarm-description>
+ <alarm-short-description>No Input Feed Selec</alarm-short-description>
+ <alarm-type>Chassis</alarm-type>
+ </alarm-detail>
+ <alarm-detail>
+ <alarm-time>
+ 2017-07-25 16:04:31 UTC
+ </alarm-time>
+ <alarm-class>Major</alarm-class>
+ <alarm-description>No Input Feed Selected for PSM 4</alarm-description>
+ <alarm-short-description>No Input Feed Selec</alarm-short-description>
+ <alarm-type>Chassis</alarm-type>
+ </alarm-detail>
+ <alarm-detail>
+ <alarm-time>
+ 2017-07-25 16:04:31 UTC
+ </alarm-time>
+ <alarm-class>Major</alarm-class>
+ <alarm-description>No Input Feed Selected for PSM 10</alarm-description>
+ <alarm-short-description>No Input Feed Selec</alarm-short-description>
+ <alarm-type>Chassis</alarm-type>
+ </alarm-detail>
+ <alarm-detail>
+ <alarm-time>
+ 2017-07-25 16:04:31 UTC
+ </alarm-time>
+ <alarm-class>Major</alarm-class>
+ <alarm-description>No Input Feed Selected for PSM 11</alarm-description>
+ <alarm-short-description>No Input Feed Selec</alarm-short-description>
+ <alarm-type>Chassis</alarm-type>
+ </alarm-detail>
+ <alarm-detail>
+ <alarm-time>
+ 2017-07-25 16:04:31 UTC
+ </alarm-time>
+ <alarm-class>Major</alarm-class>
+ <alarm-description>No Input Feed Selected for PSM 12</alarm-description>
+ <alarm-short-description>No Input Feed Selec</alarm-short-description>
+ <alarm-type>Chassis</alarm-type>
+ </alarm-detail>
+ <alarm-detail>
+ <alarm-time>
+ 2017-07-25 16:04:31 UTC
+ </alarm-time>
+ <alarm-class>Major</alarm-class>
+ <alarm-description>No Input Feed Selected for PSM 13</alarm-description>
+ <alarm-short-description>No Input Feed Selec</alarm-short-description>
+ <alarm-type>Chassis</alarm-type>
+ </alarm-detail>
+ <alarm-detail>
+ <alarm-time>
+ 2017-07-25 16:04:31 UTC
+ </alarm-time>
+ <alarm-class>Minor</alarm-class>
+ <alarm-description>VMHost RE 1 host application failed</alarm-description>
+ <alarm-short-description>VMHost RE 1 host ap</alarm-short-description>
+ <alarm-type>Chassis</alarm-type>
+ </alarm-detail>
+ <alarm-detail>
+ <alarm-time>
+ 2017-07-25 16:04:31 UTC
+ </alarm-time>
+ <alarm-class>Minor</alarm-class>
+ <alarm-description>Check plane 1 Fabric Chip</alarm-description>
+ <alarm-short-description>Check plane 1 FCHIP</alarm-short-description>
+ <alarm-type>Chassis</alarm-type>
+ </alarm-detail>
+ </alarm-information>
+</foo>
+</root>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<root xmlns="complexjson">
+ <cont1 xmlns="ns:complex:json" >
+ <lf12-any>true</lf12-any>
+ <lf12-any>
+ <baz>120</baz>
+ <baz>str-val</baz>
+ <baz>false</baz>
+ </lf12-any>
+ <lf12-any>
+ <foo />
+ </lf12-any>
+ <lf13-any>
+ <anyxml-array-a>
+ <foo>true</foo>
+ </anyxml-array-a>
+ <anyxml-array-a>10</anyxml-array-a>
+ <anyxml-array-a>
+ <bar>false</bar>
+ </anyxml-array-a>
+ <anyxml-array-b>1</anyxml-array-b>
+ <anyxml-array-b>2</anyxml-array-b>
+ <anyxml-array-b>4</anyxml-array-b>
+ <anyxml-array-b>5</anyxml-array-b>
+ <anyxml-array-b>7</anyxml-array-b>
+ </lf13-any>
+ <lf14-any>
+ <anyxml-object-a>
+ <data-a1>10</data-a1>
+ <data-a2>11</data-a2>
+ </anyxml-object-a>
+ <anyxml-object-b>
+ <child-object-b1>
+ <data-b1>5.5</data-b1>
+ </child-object-b1>
+ <child-object-b2>
+ <data-b2>b2-val</data-b2>
+ </child-object-b2>
+ </anyxml-object-b>
+ </lf14-any>
+ </cont1>
+ <root>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+<foo xmlns="bug8927.test">
+ <alarm-information xmlns="bug8927.test">
+ <alarm-summary>
+ <active-alarm-count/>
+ </alarm-summary>
+ <elmt>1</elmt>
+ <elmt>2</elmt>
+ <elmt>3</elmt>
+ </alarm-information>
+</foo>
+</root>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+<foo xmlns="bug8927.test">
+ <alarm-information xmlns="bug8927.test">
+ <alarm-summary>
+ <active-alarm-count/>
+ </alarm-summary>
+ <elmt>1</elmt>
+ <elmt>2</elmt>
+ <elmt>3</elmt>
+ <test>123</test>
+ </alarm-information>
+</foo>
+</root>
\ No newline at end of file