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)) {