From: Ruslan Kashapov Date: Tue, 10 Jan 2023 08:25:30 +0000 (+0200) Subject: Fix anyData content normalization incl no data case X-Git-Tag: v8.0.10~16 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F53%2F104453%2F2;p=yangtools.git Fix anyData content normalization incl no data case 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 Signed-off-by: Ruslan Kashapov (cherry picked from commit 68e5e4f9d8261ef5e6fd9fcf62cd84c45b8a7739) --- diff --git a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/DOMSourceAnydata.java b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/DOMSourceAnydata.java index 77b627eecd..97540504e6 100644 --- a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/DOMSourceAnydata.java +++ b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/DOMSourceAnydata.java @@ -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); diff --git a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlParserStream.java b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlParserStream.java index e4f54e4124..57be0281c4 100644 --- a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlParserStream.java +++ b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlParserStream.java @@ -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 index 0000000000..0f9fa8bf81 --- /dev/null +++ b/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnydataNormalizeContentTest.java @@ -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; + +public 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 LEAF_NODE = ImmutableNodes.leafNode(CONT_LEAF_NODEID, "test"); + + private static final String ANYDATA_XML = "test"; + private static final String ANYDATA_EMPTY_XML = ""; + + @BeforeAll + public static void beforeAll() throws Exception { + beforeClass(); // junit 4 + } + + @AfterAll + public static void afterAll() { + afterClass(); // junit 4 + } + + @ParameterizedTest(name = "Anydata normalize to {0}") + @MethodSource("normalizeArgs") + public 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()); + } + + public static Stream 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 index 6fe50b0699..0000000000 --- a/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnydataNormalizeToContainerTest.java +++ /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("" - + "" - + "somedata" - + "" - + "")); - - 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); - } -} diff --git a/codec/yang-data-codec-xml/src/test/resources/test-anydata.yang b/codec/yang-data-codec-xml/src/test/resources/test-anydata.yang index 0655955be4..a125e8b16f 100644 --- a/codec/yang-data-codec-xml/src/test/resources/test-anydata.yang +++ b/codec/yang-data-codec-xml/src/test/resources/test-anydata.yang @@ -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; + } } }