[YANGTOOLS-807] Unable to serialize augmentation with children defined Created: 18/Aug/17  Updated: 10/Apr/22  Resolved: 20/Sep/17

Status: Resolved
Project: yangtools
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Bug
Reporter: Jan Srnicek Assignee: Peter Kajsa
Resolution: Cannot Reproduce Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

Operating System: All
Platform: All


External issue ID: 9011

 Description   

Prerequisites

Model:

module hc-test {
yang-version 1;
namespace "urn:opendaylight:params:xml:ns:yang:hc:test";
prefix "hct";

revision "2015-01-05"

{ description "Testing HC model with bindings"; }

import yang-ext

{ prefix "ext"; }

// WRITE ORDER 3
// READ ORDER 1
container simple-container {
leaf simple-container-name

{ type string; }

container nested-container {
leaf nested-container-name

{ type string; }
}
}

container simple-container-with-list {
list simple-list-in-container {
key id;
leaf id { type int32; }

leaf name { type string; }
}
}

list simple-list {
key "simple-key";
leaf "simple-key"{ type string; }
leaf "simple-leaf"{ type string; }

leaf "another-simple-leaf"{ type string; }
}

container with-leaf-list {
leaf-list simple-leaf-list { type string; }

}

grouping some-attributes {
leaf leaf-from-grouping

{ type string; }

// WRITE ORDER 5
container container-from-grouping {
leaf leaf-in-container-from-grouping { type int32; }
}
}

// WRITE ORDER 9 (no real attributes though)
// READ ORDER 2
container container-with-list {
// WRITE ORDER 7
// READ ORDER 2.1
list list-in-container {
key "id";
ordered-by "user";

leaf id { type uint32; }

// WRITE ORDER 8
// READ ORDER 2.1.1
container container-in-list {
leaf name { type string; }

// WRITE ORDER 6
// READ ORDER 2.1.1.1
list nested-list {
key "nested-id";
ordered-by "user";

leaf nested-id { type string; }

leaf nested-name { type string; }
}
}
}
}

// WRITE ORDER 4
container container-with-choice {
leaf name { type string; }

uses some-attributes;

choice choice {
leaf c1

{ type string; }

leaf c2 { type string; }

// WRITE ORDER: 2
container c3 {
leaf name

{ type string; }

}
}
}

// WRITE ORDER: 2
// READ ORDER 1.1
augment "/hct:simple-container" {
ext:augment-identifier "simple-augment";

leaf simple-augment-leaf

{ type string; }
}

// WRITE UNORDERED
// READ ORDER 1.2
augment "/hct:simple-container" {
ext:augment-identifier "complex-augment";

// WRITE ORDER: 1
// READ ORDER 1.2.1
container complex-augment-container {
leaf some-leaf { type string; }
}

leaf leaf-under-aug { type string; }

}

}

Logic used

public static writeJsonNode(){
...
if(isAug(..)){
// aug
final SchemaNode augParentSchemaNode = INSTANCE.schemaForAug(schemaContext, identifier);
final SchemaPath augParentSchemaPath = augParentSchemaNode.getPath();
final NormalizedNodeStreamWriter streamWriter = JSONNormalizedNodeStreamWriter
.createNestedWriter(JSONCodecFactory.getPrecomputed(schemaContext), augParentSchemaPath, null, jsonWriter);
final NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(streamWriter, true);
normalizedNodeWriter.write(nodeData);
}
}

@Nonnull
default SchemaNode schemaForAug(@Nonnull final SchemaContext schemaContext,
@Nonnull final YangInstanceIdentifier yangId) {
return SchemaContextUtil
.findDataSchemaNode(schemaContext, SchemaPath.create(schemaPathArguments(yangId, 0), true));
}

default List<QName> schemaPathArguments(final @Nonnull YangInstanceIdentifier yangId,
final int removeLastX) {
final List<QName> all = yangId.getPathArguments().stream()
// predicate/augmentations ones must be filtered out
.filter(pathArgument -> !(pathArgument instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates

pathArgument instanceof YangInstanceIdentifier.AugmentationIdentifier)
)
.map(YangInstanceIdentifier.PathArgument::getNodeType)
.collect(Collectors.toList());
if (removeLastX == 0) { return all; }

return all.stream().limit(all.size() - removeLastX).collect(Collectors.toList());
}

Given that following data are written
First one is write of augmentation node with leaf defined
Second one is write of augmentation node with container defined

@Test
public void testWriteAugmentationWithLeaf() throws IOException {
final QName leafQname = QName.create(SimpleContainer.QNAME, "leaf-under-aug");
final ImmutableSet<QName> childNodes = ImmutableSet.of(ComplexAugmentContainer.QNAME, leafQname);
final YangInstanceIdentifier.AugmentationIdentifier augNodeIdentifier =
new YangInstanceIdentifier.AugmentationIdentifier(childNodes);
final YangInstanceIdentifier id = YangInstanceIdentifier.builder()
.node(SimpleContainer.QNAME)
.node(augNodeIdentifier)
.build();

final AugmentationNode augData = Builders.augmentationBuilder()
.withNodeIdentifier(augNodeIdentifier)
.withChild(Builders.leafBuilder().withNodeIdentifier(
NodeIdentifier.create(leafQname))
.withValue("val").build())
.build();
final ByteArrayOutputStream out = new ByteArrayOutputStream();
JsonUtils.writeJsonNode(id, augData, registry, schemaContext, out);
final String result = new String(out.toByteArray());
assertEquals("", result);
}

@Test
public void testWriteAugmentationWithContainer() throws IOException {
final QName leafQname = QName.create(SimpleContainer.QNAME, "leaf-under-aug");
final QName underAugContainerQname = ComplexAugmentContainer.QNAME;
final ImmutableSet<QName> childNodes = ImmutableSet.of(underAugContainerQname, leafQname);
final YangInstanceIdentifier.AugmentationIdentifier augNodeIdentifier =
new YangInstanceIdentifier.AugmentationIdentifier(childNodes);
final YangInstanceIdentifier id = YangInstanceIdentifier.builder()
.node(SimpleContainer.QNAME)
.node(augNodeIdentifier)
.build();

final AugmentationNode augData = Builders.augmentationBuilder()
.withNodeIdentifier(augNodeIdentifier)
.withChild(Builders.containerBuilder()
.withNodeIdentifier(NodeIdentifier.create(underAugContainerQname))
.build())
.build();
final ByteArrayOutputStream out = new ByteArrayOutputStream();
JsonUtils.writeJsonNode(id, augData, registry, schemaContext, out);
final String result = new String(out.toByteArray());
assertEquals("", result);
}

Following error is showed

java.lang.IllegalStateException: Nesting problem.

at com.google.gson.stream.JsonWriter.beforeName(JsonWriter.java:616)
at com.google.gson.stream.JsonWriter.writeDeferredName(JsonWriter.java:401)
at com.google.gson.stream.JsonWriter.beginObject(JsonWriter.java:307)
at org.opendaylight.yangtools.yang.data.codec.gson.JSONStreamWriterObjectContext.emitStart(JSONStreamWriterObjectContext.java:28)
at org.opendaylight.yangtools.yang.data.codec.gson.JSONStreamWriterNamedObjectContext.emitStart(JSONStreamWriterNamedObjectContext.java:29)
at org.opendaylight.yangtools.yang.data.codec.gson.JSONStreamWriterContext.emitMyself(JSONStreamWriterContext.java:118)
at org.opendaylight.yangtools.yang.data.codec.gson.JSONStreamWriterContext.endNode(JSONStreamWriterContext.java:148)
at org.opendaylight.yangtools.yang.data.codec.gson.JSONNormalizedNodeStreamWriter.endNode(JSONNormalizedNodeStreamWriter.java:217)
at org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter.writeChildren(NormalizedNodeWriter.java:171)
at org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter.wasProcessedAsCompositeNode(NormalizedNodeWriter.java:193)
at org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter.write(NormalizedNodeWriter.java:97)
at org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter.writeChildren(NormalizedNodeWriter.java:168)
at org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter.wasProcessedAsCompositeNode(NormalizedNodeWriter.java:220)
at org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter.write(NormalizedNodeWriter.java:97)
at io.fd.honeycomb.data.processing.JsonUtils.write(JsonUtils.java:123)
at io.fd.honeycomb.data.processing.JsonUtils.writeJsonNode(JsonUtils.java:100)
at io.fd.honeycomb.data.processing.JsonUtilsSerializationTest.testWriteAugmentationWithContainer(JsonUtilsSerializationTest.java:156)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

With exactly same augmentaion data ,but writing it as part of parent container, no error is showed and correct data returned(see code bellow)

@Test
public void testWriteContainerWithAugData() throws IOException {
final QName leafQname = QName.create(SimpleContainer.QNAME, "leaf-under-aug");
final QName underAugContainerQname = ComplexAugmentContainer.QNAME;
final ImmutableSet<QName> childNodes = ImmutableSet.of(underAugContainerQname, leafQname);

final YangInstanceIdentifier.AugmentationIdentifier augNodeIdentifier =
new YangInstanceIdentifier.AugmentationIdentifier(childNodes);
final YangInstanceIdentifier id = YangInstanceIdentifier.of(SimpleContainer.QNAME);
final AugmentationNode augData = Builders.augmentationBuilder()
.withNodeIdentifier(augNodeIdentifier)
.withChild(Builders.containerBuilder()
.withNodeIdentifier(NodeIdentifier.create(underAugContainerQname))
.build())
.build();
NormalizedNode<?, ?> data = Builders.containerBuilder()
.withNodeIdentifier(NodeIdentifier.create(SimpleContainer.QNAME))
.withChild(augData)
.build();
final ByteArrayOutputStream out = new ByteArrayOutputStream();
JsonUtils.writeJsonNode(id, data, registry, schemaContext, out);
final String result = new String(out.toByteArray());
assertEquals("{\"hc-test:simple-container\":{\"complex-augment-container\":{}}}", result);
}



 Comments   
Comment by Jan Srnicek [ 06/Sep/17 ]

Could this be backported to Carbon?

Comment by Peter Kajsa [ 07/Sep/17 ]

Yes, once we would have a fix, we will try to backport it to the carbon..

Comment by Peter Kajsa [ 20/Sep/17 ]

I have tested reported issue many times from different points of view on different branches and it always works fine for me. Here you can find the last unit test I used to test this issue (https://git.opendaylight.org/gerrit/#/c/63328/). The only problem I noticed is that in testWriteContainerWithAugData "id" and "data" points to the same container i.e. SimpleContainer.QNAME what fails if it is called directly in YT. Therefore I am closing this bug as "works for me"..

If this issue still persists, please reopen the bug with additional info how it could be replicated from YT point of view.

Generated at Wed Feb 07 20:54:22 UTC 2024 using Jira 8.20.10#820010-sha1:ace47f9899e9ee25d7157d59aa17ab06aee30d3d.