Fix anyData content normalization incl no data case 52/104452/2
authorRuslan Kashapov <ruslan.kashapov@pantheon.tech>
Tue, 10 Jan 2023 08:25:30 +0000 (10:25 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Tue, 21 Feb 2023 11:53:54 +0000 (12:53 +0100)
Parsing of anydata content is now performed with inference
pointing to anydata node itself as a root (parentNode),
not first child. This allows proper normalization of any
anydata content incl empty case.

List/leaf-list element parsing logic was updated to detach
schema node value check as a condition bc top level node
identifiers are not matching when anydata content is parsed.
Local name and namespace match is used instead.

JIRA: YANGTOOLS-1011
Change-Id: I09ae84c44ada198b888d901dff0425b29400fb4f
Signed-off-by: Anna Bencurova <Anna.Bencurova@pantheon.tech>
Signed-off-by: Ruslan Kashapov <ruslan.kashapov@pantheon.tech>
(cherry picked from commit 68e5e4f9d8261ef5e6fd9fcf62cd84c45b8a7739)

codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/DOMSourceAnydata.java
codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlParserStream.java
codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnydataNormalizeContentTest.java [new file with mode: 0644]
codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnydataNormalizeToContainerTest.java [deleted file]
codec/yang-data-codec-xml/src/test/resources/test-anydata.yang

index 77b627eecd4457e4ecbd9287ddfaa67066289c64..97540504e664ac29a13d9858ee9d55e8622dfe20 100644 (file)
@@ -59,8 +59,6 @@ final class DOMSourceAnydata extends AbstractNormalizableAnydata {
 
         try {
             final XMLStreamReader reader = toStreamReader();
-            reader.nextTag();
-
             xmlParser.parse(reader).flush();
         } catch (XMLStreamException | URISyntaxException | SAXException e) {
             throw new IOException("Failed to parse payload", e);
index e4f54e4124742b789c4fe494584b093f905ae7e2..57be0281c4400dd74d358265b115a98bb744307b 100644 (file)
@@ -473,16 +473,17 @@ public final class XmlParserStream implements Closeable, Flushable {
         }
 
         if (parent instanceof LeafListNodeDataWithSchema || parent instanceof ListNodeDataWithSchema) {
-            String xmlElementName = in.getLocalName();
-            while (xmlElementName.equals(parent.getSchema().getQName().getLocalName())) {
+            final String localName = in.getLocalName();
+            final String namespaceURI = in.getNamespaceURI();
+            // aggregate current and subsequent nodes having same localName and namespace
+            // into set of entries belonging to current parent node
+            while (localName.equals(in.getLocalName()) && namespaceURI.equals(in.getNamespaceURI())) {
                 read(in, newEntryNode(parent), rootElement);
                 if (in.getEventType() == XMLStreamConstants.END_DOCUMENT
                         || in.getEventType() == XMLStreamConstants.END_ELEMENT) {
                     break;
                 }
-                xmlElementName = in.getLocalName();
             }
-
             return;
         }
 
diff --git a/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnydataNormalizeContentTest.java b/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnydataNormalizeContentTest.java
new file mode 100644 (file)
index 0000000..a7242b4
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * 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.xml;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.util.stream.Stream;
+import javax.xml.stream.XMLStreamReader;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.Empty;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.AnydataNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedAnydata;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
+
+class AnydataNormalizeContentTest extends AbstractAnydataTest {
+
+    private static final QName BAR_QNAME = QName.create(FOO_QNAME, "bar"); // container level 2
+    private static final QName LIST_QNAME = QName.create(FOO_QNAME, "lst"); // list
+    private static final QName LEAF_LIST_QNAME = QName.create(FOO_QNAME, "my-leafs"); // leaf-list of type string
+    private static final QName LEAF_EMPTY_QNAME = QName.create(FOO_QNAME, "empty-leaf"); // leaf of type empty
+    private static final NodeIdentifier BAR_NODEID = NodeIdentifier.create(BAR_QNAME);
+    private static final NodeIdentifier LIST_NODEID = NodeIdentifier.create(LIST_QNAME);
+    private static final NodeIdentifier LEAF_LIST_NODEID = NodeIdentifier.create(LEAF_LIST_QNAME);
+    private static final NodeIdentifier LEAF_EMPTY_NODEID = NodeIdentifier.create(LEAF_EMPTY_QNAME);
+    private static final LeafNode<String> LEAF_NODE = ImmutableNodes.leafNode(CONT_LEAF_NODEID, "test");
+
+    private static final String ANYDATA_XML = "<foo xmlns=\"test-anydata\"><cont-leaf>test</cont-leaf></foo>";
+    private static final String ANYDATA_EMPTY_XML = "<foo xmlns=\"test-anydata\" />";
+
+    @BeforeAll
+    static void beforeAll() throws Exception {
+        beforeClass(); // junit 4
+    }
+
+    @AfterAll
+    static void afterAll() {
+        afterClass(); // junit 4
+    }
+
+    @ParameterizedTest(name = "Anydata normalize to {0}")
+    @MethodSource("normalizeArgs")
+    void anydataNormalize(final String testDesc, final String xml, final Inference inference,
+            final NormalizedNode expectedData) throws Exception {
+
+        final XMLStreamReader reader = UntrustedXML.createXMLStreamReader(toInputStream(xml));
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final XmlParserStream xmlParser =
+                XmlParserStream.create(streamWriter, Inference.ofDataTreePath(SCHEMA_CONTEXT, FOO_QNAME));
+        xmlParser.parse(reader);
+
+        final AnydataNode<?> anydataNode = assertInstanceOf(AnydataNode.class, result.getResult());
+        final DOMSourceAnydata domSourceAnydata = assertInstanceOf(DOMSourceAnydata.class, anydataNode.body());
+
+        final NormalizedAnydata normalizedAnydata = domSourceAnydata.normalizeTo(inference);
+        assertNotNull(normalizedAnydata);
+        assertEquals(expectedData, normalizedAnydata.getData());
+    }
+
+    private static Stream<Arguments> normalizeArgs() {
+        // test case descriptor, xml, inference, expected normalized data
+        return Stream.of(
+                Arguments.of("container (root level)",
+                        ANYDATA_XML,
+                        Inference.ofDataTreePath(SCHEMA_CONTEXT, CONT_QNAME),
+                        Builders.containerBuilder().withNodeIdentifier(CONT_NODEID).withChild(LEAF_NODE).build()),
+                Arguments.of("container (level 2)",
+                        ANYDATA_XML,
+                        Inference.ofDataTreePath(SCHEMA_CONTEXT, CONT_QNAME, BAR_QNAME),
+                        Builders.containerBuilder().withNodeIdentifier(BAR_NODEID).withChild(LEAF_NODE).build()),
+                Arguments.of("empty container",
+                        ANYDATA_EMPTY_XML,
+                        Inference.ofDataTreePath(SCHEMA_CONTEXT, CONT_QNAME, BAR_QNAME),
+                        ImmutableNodes.containerNode(BAR_NODEID)),
+                Arguments.of("single list element",
+                        ANYDATA_XML,
+                        Inference.ofDataTreePath(SCHEMA_CONTEXT, LIST_QNAME),
+                        Builders.unkeyedListBuilder().withNodeIdentifier(LIST_NODEID).withChild(
+                                Builders.unkeyedListEntryBuilder().withNodeIdentifier(LIST_NODEID)
+                                        .withChild(LEAF_NODE).build()).build()),
+                Arguments.of("single empty list element",
+                        ANYDATA_EMPTY_XML,
+                        Inference.ofDataTreePath(SCHEMA_CONTEXT, LIST_QNAME),
+                        Builders.unkeyedListBuilder().withNodeIdentifier(LIST_NODEID).withChild(
+                                Builders.unkeyedListEntryBuilder().withNodeIdentifier(LIST_NODEID).build()).build()),
+                Arguments.of("single empty leaf-list element",
+                        ANYDATA_EMPTY_XML,
+                        Inference.ofDataTreePath(SCHEMA_CONTEXT, LIST_QNAME, LEAF_LIST_QNAME),
+                        Builders.leafSetBuilder().withNodeIdentifier(LEAF_LIST_NODEID).withChild(
+                                Builders.leafSetEntryBuilder()
+                                        .withNodeIdentifier(new NodeWithValue<>(LEAF_LIST_QNAME, ""))
+                                        .withValue("").build()).build()),
+                Arguments.of("leaf of type empty",
+                        ANYDATA_EMPTY_XML,
+                        Inference.ofDataTreePath(SCHEMA_CONTEXT, CONT_QNAME, LEAF_EMPTY_QNAME),
+                        ImmutableNodes.leafNode(LEAF_EMPTY_NODEID, Empty.value())));
+    }
+}
diff --git a/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnydataNormalizeToContainerTest.java b/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnydataNormalizeToContainerTest.java
deleted file mode 100644 (file)
index 6fe50b0..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * 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.xml;
-
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertNotNull;
-
-import javax.xml.stream.XMLStreamReader;
-import org.junit.Test;
-import org.opendaylight.yangtools.util.xml.UntrustedXML;
-import org.opendaylight.yangtools.yang.data.api.schema.AnydataNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedAnydata;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
-import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
-import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
-import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.SchemaNode;
-import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
-import org.opendaylight.yangtools.yang.model.spi.DefaultSchemaTreeInference;
-import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
-
-public class AnydataNormalizeToContainerTest extends AbstractAnydataTest {
-    @Test
-    public void testAnydataNormalizeToContainer() throws Exception {
-        //Create Data Scheme from yang file
-        final SchemaNode fooSchemaNode = SCHEMA_CONTEXT.findDataTreeChild(FOO_QNAME).orElse(null);
-        assertThat(fooSchemaNode, instanceOf(AnydataSchemaNode.class));
-        final AnydataSchemaNode anyDataSchemaNode = (AnydataSchemaNode) fooSchemaNode;
-
-        final SchemaNode barSchemaNode = SCHEMA_CONTEXT.findDataTreeChild(CONT_QNAME).orElse(null);
-        assertThat(barSchemaNode, instanceOf(ContainerSchemaNode.class));
-        final ContainerSchemaNode containerSchemaNode = (ContainerSchemaNode) barSchemaNode;
-
-        // deserialization
-        final XMLStreamReader reader
-                = UntrustedXML.createXMLStreamReader(toInputStream("<foo xmlns=\"test-anydata\">"
-                                                                  +     "<bar xmlns=\"test-anydata\">"
-                                                                  +         "<cont-leaf>somedata</cont-leaf>"
-                                                                  +     "</bar>"
-                                                                  + "</foo>"));
-
-        final NormalizedNodeResult result = new NormalizedNodeResult();
-        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
-        final XmlParserStream xmlParser = XmlParserStream.create(streamWriter,
-            Inference.ofDataTreePath(SCHEMA_CONTEXT, FOO_QNAME));
-        xmlParser.parse(reader);
-
-        final NormalizedNode transformedInput = result.getResult();
-        assertThat(transformedInput, instanceOf(AnydataNode.class));
-        AnydataNode<?> anydataNode = (AnydataNode<?>) transformedInput;
-
-        //Normalize anydata content to specific container element
-        DOMSourceAnydata domSourceAnydata = (DOMSourceAnydata) anydataNode.body();
-        NormalizedAnydata normalizedAnydata = domSourceAnydata.normalizeTo(
-            DefaultSchemaTreeInference.of(SCHEMA_CONTEXT, Absolute.of(CONT_QNAME)));
-        assertNotNull(normalizedAnydata);
-    }
-}
index 0655955be45c9359f6922bc2327619f8b21c256e..a125e8b16f0c1d3cc5b30219e3934b437a320f6f 100644 (file)
@@ -11,6 +11,22 @@ module test-anydata {
         leaf cont-leaf {
             type string;
         }
+        leaf empty-leaf {
+            type empty;
+        }
+        container bar {
+            leaf cont-leaf {
+                type string;
+            }
+        }
+    }
+    list lst {
+        leaf cont-leaf {
+            type string;
+        }
+        leaf-list my-leafs {
+            type string;
+        }
     }
 }