From 33eb1c6972790f5e8912e935e01dacaae27ae45a Mon Sep 17 00:00:00 2001 From: prakanth <50439067+prakanth97@users.noreply.github.com> Date: Thu, 21 Mar 2024 19:08:33 +0530 Subject: [PATCH] Handle projection in json or anydata array --- ballerina/tests/fromXml_test.bal | 32 +++++ ballerina/tests/from_xml_with_options.bal | 123 ++++++++++++++++++ .../compiler/XmldataRecordFieldValidator.java | 9 +- .../stdlib/data/xmldata/utils/DataUtils.java | 6 +- .../stdlib/data/xmldata/xml/XmlParser.java | 28 +++- .../stdlib/data/xmldata/xml/XmlTraversal.java | 2 +- 6 files changed, 189 insertions(+), 11 deletions(-) diff --git a/ballerina/tests/fromXml_test.bal b/ballerina/tests/fromXml_test.bal index 59ec2f24..6c47eada 100644 --- a/ballerina/tests/fromXml_test.bal +++ b/ballerina/tests/fromXml_test.bal @@ -2753,6 +2753,38 @@ function testAddingContentFieldWhenRestTypeAsExpTypeForFromXmlWithType() returns test:assertEquals(val2.B, {C: {\#content: 3, c: "attribute_c"}}); } +type ProjectionRec1 record {| + json[1] Employee; + anydata[2] Departments; +|}; + +@test:Config +function testProjectionWithJsonArrayOrAnydataArrayForFromXmlStringWithType() returns error? { + string xmlStr = string ` + + John Doe + + + Walter White + + Engineering + Hr + Customer Success + `; + record { + json[1] Employee; + anydata[2] Departments; + } rec = check fromXmlStringWithType(xmlStr); + test:assertEquals(rec.length(), 2); + test:assertEquals(rec.Employee, [ + { + "Name": "John Doe", + "age": 30 + } + ]); + test:assertEquals(rec.Departments, ["Engineering", "Hr"]); +} + // Negative cases type DataN1 record {| int A; diff --git a/ballerina/tests/from_xml_with_options.bal b/ballerina/tests/from_xml_with_options.bal index b03beb14..1e6d0411 100644 --- a/ballerina/tests/from_xml_with_options.bal +++ b/ballerina/tests/from_xml_with_options.bal @@ -228,6 +228,129 @@ function testDisableProjectionForFromXmlWithTypeNegative() returns error? { test:assertEquals((e5).message(), "undefined field '#content' in record 'data.xmldata:record {| int age; |}'"); } +type IntArray int[2]; +type StringArray string[3]; +type Record1 record {| + IntArray A; + StringArray B; +|}; + +@test:Config +function testDisableProjectionInArrayForFromXmlStringWithTypeNegative() { + string xmlStr = string ` + 1 + 2 + 3 + `; + DataProj|Error err1 = fromXmlStringWithType(xmlStr, sOptions3); + test:assertTrue(err1 is Error); + test:assertEquals(( err1).message(), "array size is not compatible with the expected size"); + + string xmlStr2 = string ` + + 1 + 2 + 3 + Kevin + Violet + Tommy + + `; + Record1|Error err2 = fromXmlStringWithType(xmlStr2, sOptions3); + test:assertTrue(err2 is Error); + test:assertEquals(( err2).message(), "array size is not compatible with the expected size"); + + string xmlStr3 = string ` + + 1 + 2 + Kevin + Violet + Tommy + James + + `; + Record1|Error err3 = fromXmlStringWithType(xmlStr3, sOptions3); + test:assertTrue(err3 is Error); + test:assertEquals(( err3).message(), "array size is not compatible with the expected size"); + + string xmlStr4 = string ` + + John Doe + + + Walter White + + Engineering + Hr + Customer Success + `; + record { + json[1] Employee; + anydata[2] Departments; + }|error rec = fromXmlStringWithType(xmlStr4, sOptions3); + test:assertTrue(rec is error); + test:assertEquals((rec).message(), "array size is not compatible with the expected size"); +} + +@test:Config +function testDisableProjectionInArrayForFromXmlWithTypeNegative() { + xml xmlVal1 = xml ` + 1 + 2 + 3 + `; + DataProj|Error err1 = fromXmlWithType(xmlVal1, sOptions3); + test:assertTrue(err1 is Error); + test:assertEquals(( err1).message(), "array size is not compatible with the expected size"); + + xml xmlVal2 = xml ` + + 1 + 2 + 3 + Kevin + Violet + Tommy + + `; + Record1|Error err2 = fromXmlWithType(xmlVal2, sOptions3); + test:assertTrue(err2 is Error); + test:assertEquals(( err2).message(), "array size is not compatible with the expected size"); + + xml xmlVal3 = xml ` + + 1 + 2 + Kevin + Violet + Tommy + James + + `; + Record1|Error err3 = fromXmlWithType(xmlVal3, sOptions3); + test:assertTrue(err3 is Error); + test:assertEquals(( err3).message(), "array size is not compatible with the expected size"); + + xml xmlVal4 = xml ` + + John Doe + + + Walter White + + Engineering + Hr + Customer Success + `; + record { + json[1] Employee; + anydata[2] Departments; + }|error rec = fromXmlWithType(xmlVal4, sOptions3); + test:assertTrue(rec is error); + test:assertEquals((rec).message(), "array size is not compatible with the expected size"); +} + SourceOptions sOptions6 = {attributePrefix: "@", allowDataProjection: false, textFieldName: "value"}; type Library record {| diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/data/xmldata/compiler/XmldataRecordFieldValidator.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/data/xmldata/compiler/XmldataRecordFieldValidator.java index a12f2713..9957ca62 100644 --- a/compiler-plugin/src/main/java/io/ballerina/stdlib/data/xmldata/compiler/XmldataRecordFieldValidator.java +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/data/xmldata/compiler/XmldataRecordFieldValidator.java @@ -149,7 +149,7 @@ private void validateExpectedType(TypeSymbol typeSymbol, Optional loca case UNION -> { int nonErrorTypeCount = 0; for (TypeSymbol memberTSymbol : ((UnionTypeSymbol) typeSymbol).memberTypeDescriptors()) { - if (memberTSymbol.typeKind() == TypeDescKind.ERROR) { + if (getReferredTypeSymbol(memberTSymbol).typeKind() == TypeDescKind.ERROR) { continue; } nonErrorTypeCount++; @@ -162,6 +162,13 @@ private void validateExpectedType(TypeSymbol typeSymbol, Optional loca } } + private TypeSymbol getReferredTypeSymbol(TypeSymbol typeSymbol) { + if (typeSymbol.typeKind() == TypeDescKind.TYPE_REFERENCE) { + return ((TypeReferenceTypeSymbol) typeSymbol).typeDescriptor(); + } + return typeSymbol; + } + private boolean isNotValidExpectedType(TypeSymbol typeSymbol) { switch (typeSymbol.typeKind()) { case RECORD -> { diff --git a/native/src/main/java/io/ballerina/stdlib/data/xmldata/utils/DataUtils.java b/native/src/main/java/io/ballerina/stdlib/data/xmldata/utils/DataUtils.java index 2093e51c..61da60e7 100644 --- a/native/src/main/java/io/ballerina/stdlib/data/xmldata/utils/DataUtils.java +++ b/native/src/main/java/io/ballerina/stdlib/data/xmldata/utils/DataUtils.java @@ -200,10 +200,12 @@ public static QualifiedName getElementName(QName qName) { public static Object convertStringToExpType(BString value, Type expType) { Object result; - switch (expType.getTag()) { + Type refferedType = TypeUtils.getReferredType(expType); + switch (refferedType.getTag()) { case TypeTags.ANYDATA_TAG, TypeTags.ANY_TAG, TypeTags.JSON_TAG -> result = FromString.fromStringWithType(value, PredefinedTypes.TYPE_JSON); - case TypeTags.ARRAY_TAG -> result = convertStringToExpType(value, ((ArrayType) expType).getElementType()); + case TypeTags.ARRAY_TAG -> result = convertStringToExpType(value, + ((ArrayType) refferedType).getElementType()); default -> result = FromString.fromStringWithType(value, expType); } diff --git a/native/src/main/java/io/ballerina/stdlib/data/xmldata/xml/XmlParser.java b/native/src/main/java/io/ballerina/stdlib/data/xmldata/xml/XmlParser.java index c9f43bf0..26afbe8a 100644 --- a/native/src/main/java/io/ballerina/stdlib/data/xmldata/xml/XmlParser.java +++ b/native/src/main/java/io/ballerina/stdlib/data/xmldata/xml/XmlParser.java @@ -342,6 +342,10 @@ private void addTextToCurrentNodeIfExpTypeIsArray(ArrayType fieldType, BString b bText, xmlParserData); case TypeTags.ANYDATA_TAG, TypeTags.JSON_TAG -> { BArray tempArr = (BArray) ((BMap) xmlParserData.nodesStack.peek()).get(bFieldName); + Object nextValue = tempArr.get(tempArr.getLength() - 1); + if (!(nextValue instanceof BMap)) { + return; + } tempArr.add(tempArr.getLength() - 1, convertStringToRestExpType(bText, fieldType)); } } @@ -524,7 +528,7 @@ private void updateNextArrayMember(XMLStreamReader xmlStreamReader, XmlParserDat case TypeTags.RECORD_TYPE_TAG -> updateNextRecord(xmlStreamReader, xmlParserData, fieldName, fieldType, (RecordType) type); case TypeTags.MAP_TAG, TypeTags.ANYDATA_TAG, TypeTags.JSON_TAG -> - updateNextMap(xmlParserData, fieldName, type); + updateNextMap(xmlParserData, fieldName, fieldType); } } @@ -538,12 +542,17 @@ private void updateNextMap(XmlParserData xmlParserData, String fieldName, Type f private BMap updateNextMapValue(XmlParserData xmlParserData, String fieldName, Type fieldType) { BMap nextValue; - if (fieldType.getTag() == TypeTags.MAP_TAG) { - nextValue = ValueCreator.createMapValue((MapType) fieldType); - xmlParserData.restTypes.push(((MapType) fieldType).getConstrainedType()); + Type type = fieldType; + if (fieldType.getTag() == TypeTags.ARRAY_TAG) { + type = ((ArrayType) fieldType).getElementType(); + } + + if (type.getTag() == TypeTags.MAP_TAG) { + nextValue = ValueCreator.createMapValue((MapType) type); + xmlParserData.restTypes.push(((MapType) type).getConstrainedType()); } else { - nextValue = ValueCreator.createMapValue(TypeCreator.createMapType(fieldType)); - xmlParserData.restTypes.push(fieldType); + nextValue = ValueCreator.createMapValue(TypeCreator.createMapType(type)); + xmlParserData.restTypes.push(type); } xmlParserData.attributeHierarchy.push(new HashMap<>()); @@ -552,7 +561,7 @@ private BMap updateNextMapValue(XmlParserData xmlParserData, St BMap currentNode = xmlParserData.currentNode; Object temp = currentNode.get(StringUtils.fromString(fieldName)); if (temp instanceof BArray) { - int arraySize = ((ArrayType) TypeUtils.getType(temp)).getSize(); + int arraySize = getArraySize(fieldType, temp); if (arraySize > ((BArray) temp).getLength() || arraySize == -1) { ((BArray) temp).append(nextValue); } else { @@ -565,6 +574,11 @@ private BMap updateNextMapValue(XmlParserData xmlParserData, St return nextValue; } + private int getArraySize(Type type, Object temp) { + return type.getTag() == TypeTags.ARRAY_TAG ? + ((ArrayType) type).getSize() : ((ArrayType) TypeUtils.getType(temp)).getSize(); + } + private void updateNextRecord(XMLStreamReader xmlStreamReader, XmlParserData xmlParserData, String fieldName, Type fieldType, RecordType recordType) { xmlParserData.parents.push(xmlParserData.siblings); diff --git a/native/src/main/java/io/ballerina/stdlib/data/xmldata/xml/XmlTraversal.java b/native/src/main/java/io/ballerina/stdlib/data/xmldata/xml/XmlTraversal.java index 3aa7bb8d..9b43159a 100644 --- a/native/src/main/java/io/ballerina/stdlib/data/xmldata/xml/XmlTraversal.java +++ b/native/src/main/java/io/ballerina/stdlib/data/xmldata/xml/XmlTraversal.java @@ -128,7 +128,7 @@ private void convertText(String text, XmlAnalyzerData analyzerData) { } BString fieldName = StringUtils.fromString(currentField.getFieldName()); - Type fieldType = currentField.getFieldType(); + Type fieldType = TypeUtils.getReferredType(currentField.getFieldType()); Object convertedValue = DataUtils.convertStringToExpType(StringUtils.fromString(text), fieldType); if (mapValue.containsKey(fieldName)) {