From 7180563d184d92293f42fc8fdfe1272f7871be8c Mon Sep 17 00:00:00 2001 From: Danesh Kuruppu Date: Wed, 11 Dec 2024 11:51:41 +0530 Subject: [PATCH 1/3] Support xsi:nil Attribute --- ballerina/tests/xml_to_json_test.bal | 13 +++++++++++++ .../io/ballerina/stdlib/xmldata/XmlToJson.java | 16 ++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/ballerina/tests/xml_to_json_test.bal b/ballerina/tests/xml_to_json_test.bal index aaa246af..af07489e 100644 --- a/ballerina/tests/xml_to_json_test.bal +++ b/ballerina/tests/xml_to_json_test.bal @@ -730,3 +730,16 @@ function testToJsonComplexXmlElementWithBackSlash() returns Error? { }; test:assertEquals(j, expectedOutput, msg = "testToJsonComplexXmlElement result incorrect"); } + +@test:Config { + groups: ["toJson"] +} +function testToJsonWithNilElementAndWithoutPreserveNS() returns Error? { + xml x1 = xml `Sherlock Holmes +
+ + English +
`; + json j = check toJson(x, {preserveNamespaces: false}); + test:assertEquals(j, {"name":"Sherlock Holmes", "details":{"author":null, "language":"English"}}, msg = "testToJsonWithNilElement result incorrect"); +} diff --git a/native/src/main/java/io/ballerina/stdlib/xmldata/XmlToJson.java b/native/src/main/java/io/ballerina/stdlib/xmldata/XmlToJson.java index 085704b8..a4c652a2 100644 --- a/native/src/main/java/io/ballerina/stdlib/xmldata/XmlToJson.java +++ b/native/src/main/java/io/ballerina/stdlib/xmldata/XmlToJson.java @@ -71,6 +71,8 @@ public class XmlToJson { private static final String EMPTY_STRING = ""; public static final int NS_PREFIX_BEGIN_INDEX = BXmlItem.XMLNS_NS_URI_PREFIX.length(); private static final String COLON = ":"; + public static final String XMLSCHEMA_INSTANCE_NIL = "{http://www.w3.org/2001/XMLSchema-instance}nil"; + public static final BString BSTRING_XMLSCHEMA_INSTANCE_NIL = fromString(XMLSCHEMA_INSTANCE_NIL); /** * Converts an XML to the corresponding JSON representation. @@ -190,7 +192,13 @@ private static Object convertElement(BXmlItem xmlItem, String attributePrefix, children = convertToArray(fieldType, children); } fieldDetail.setParentArrayName(fieldName); - return insertDataToMap(childrenData, children, rootNode, fieldName, fieldType, fieldDetail); + boolean nilValue = false; + // If the children is null and has `{http://www.w3.org/2001/XMLSchema-instance}nil` attribute set to true, + // then it is a nil value + if (children == null && attributeMap.containsKey(BSTRING_XMLSCHEMA_INSTANCE_NIL)) { + nilValue = Boolean.parseBoolean(attributeMap.get(BSTRING_XMLSCHEMA_INSTANCE_NIL).getValue()); + } + return insertDataToMap(childrenData, children, rootNode, fieldName, fieldType, fieldDetail, nilValue); } private static void processAttributeWithAnnotation(BXmlItem xmlItem, String attributePrefix, @@ -224,7 +232,7 @@ private static void processAttributeWithAnnotation(BXmlItem xmlItem, String attr @SuppressWarnings("unchecked") private static BMap insertDataToMap(BMap childrenData, Object children, BMap rootNode, String keyValue, - Type fieldType, FieldDetails fieldDetails) throws Exception { + Type fieldType, FieldDetails fieldDetails, boolean nilValue) throws Exception { if (childrenData.size() > 0) { if (children instanceof BMap) { BMap data = (BMap) children; @@ -248,9 +256,9 @@ private static BMap insertDataToMap(BMap child put(rootNode, keyValue, children); } else if (children == null) { if (fieldType instanceof ReferenceType && TypeUtils.getReferredType(fieldType) instanceof RecordType) { - put(rootNode, keyValue, ValueCreator.createMapValue(Constants.JSON_MAP_TYPE)); + put(rootNode, keyValue, nilValue ? null : ValueCreator.createMapValue(Constants.JSON_MAP_TYPE)); } else { - putAsFieldTypes(rootNode, keyValue, EMPTY_STRING, fieldType, fieldDetails); + putAsFieldTypes(rootNode, keyValue, nilValue ? null : EMPTY_STRING, fieldType, fieldDetails); } } else if (children instanceof BArray) { put(rootNode, keyValue, children); From 30935afad5cc3c9e3d6467030fb73764b580903b Mon Sep 17 00:00:00 2001 From: Danesh Kuruppu Date: Wed, 11 Dec 2024 16:17:50 +0530 Subject: [PATCH 2/3] Support xsi:nil Attribute in xml to json/record conversion --- ballerina/tests/from_xml_test.bal | 62 +++++++++++++++++++ ballerina/tests/xml_from_json_test.bal | 10 +++ ballerina/tests/xml_to_json_test.bal | 16 ++++- .../ballerina/stdlib/xmldata/XmlToJson.java | 5 +- 4 files changed, 90 insertions(+), 3 deletions(-) diff --git a/ballerina/tests/from_xml_test.bal b/ballerina/tests/from_xml_test.bal index 276a2485..cfe61828 100644 --- a/ballerina/tests/from_xml_test.bal +++ b/ballerina/tests/from_xml_test.bal @@ -1294,3 +1294,65 @@ isolated function testFromXmlWithNameAnnotation() returns error? { Appointments result = check fromXml(xmlPayload); test:assertEquals(result, expected, msg = "testFromXmlWithNameAnnotation result incorrect"); } + +@Name { + value: "book" +} +type Book record {| + string name; + BookDetails details; +|}; + +type BookDetails record {| + string? author; + string language; +|}; + +@test:Config { + groups: ["fromXml"] +} +function testFromXmlWithNilElementAndWithoutPreserveNS() returns Error? { + xml x1 = xml `Sherlock Holmes +
+ + English +
`; + Book expected = { + name: "Sherlock Holmes", + details: { + author: (), + language: "English" + } + }; + Book result = check fromXml(x1); + test:assertEquals(result, expected, msg = "testFromXmlWithNilElement result incorrect"); +} + +@Name { + value: "book" +} +type Book1 record {| + string name; + BookDetails1 details; +|}; + +type BookDetails1 record {| + string author; + string language; +|}; + +@test:Config { + groups: ["fromXml"] +} +function testFromXmlWithNilElementAndWithoutNillableField() returns Error? { + xml x1 = xml `Sherlock Holmes +
+ + English +
`; + Book1|Error result = fromXml(x1); + test:assertTrue(result is error, msg = "testFromXmlWithNilElementAndWithoutNillableField result incorrect"); + if (result is error) { + test:assertTrue(result.message().includes("field 'details.author' in record 'xmldata:BookDetails1' should be of type 'string', found '()'")); + } +} diff --git a/ballerina/tests/xml_from_json_test.bal b/ballerina/tests/xml_from_json_test.bal index eb1ff332..98271dcc 100644 --- a/ballerina/tests/xml_from_json_test.bal +++ b/ballerina/tests/xml_from_json_test.bal @@ -1096,3 +1096,13 @@ isolated function testWithRootTagConfig() { test:assertFail("failed to convert json to xml"); } } + +@test:Config { + groups: ["fromJson"] +} +function testFromJsonWithNull() returns error? { + json data = null; + xml? result = check fromJson({"name":"Sherlock Holmes", "details":{"author":null, "language":"English"}}, {rootTag: "book"}); + xml expected = xml `Sherlock Holmes
English
`; + test:assertEquals(result, expected, msg = "testFromJsonWithNull result incorrect"); +} diff --git a/ballerina/tests/xml_to_json_test.bal b/ballerina/tests/xml_to_json_test.bal index af07489e..69d9b5a0 100644 --- a/ballerina/tests/xml_to_json_test.bal +++ b/ballerina/tests/xml_to_json_test.bal @@ -740,6 +740,20 @@ function testToJsonWithNilElementAndWithoutPreserveNS() returns Error? { English `; - json j = check toJson(x, {preserveNamespaces: false}); + json j = check toJson(x1, {preserveNamespaces: false}); test:assertEquals(j, {"name":"Sherlock Holmes", "details":{"author":null, "language":"English"}}, msg = "testToJsonWithNilElement result incorrect"); } + +@test:Config { + groups: ["toJson"] +} +function testToJsonWithNilElementAndPreserveNS() returns Error? { + xml x1 = xml `Sherlock Holmes +
+ + English +
`; + json j = check toJson(x1, {preserveNamespaces: true}); + test:assertEquals(j, {"name":"Sherlock Holmes","details":{"author":{"@xsi:nil":"true"},"language":"English", + "@xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance"}}, msg = "testToJsonWithNilElement result incorrect"); +} diff --git a/native/src/main/java/io/ballerina/stdlib/xmldata/XmlToJson.java b/native/src/main/java/io/ballerina/stdlib/xmldata/XmlToJson.java index a4c652a2..a7c6d935 100644 --- a/native/src/main/java/io/ballerina/stdlib/xmldata/XmlToJson.java +++ b/native/src/main/java/io/ballerina/stdlib/xmldata/XmlToJson.java @@ -232,7 +232,8 @@ private static void processAttributeWithAnnotation(BXmlItem xmlItem, String attr @SuppressWarnings("unchecked") private static BMap insertDataToMap(BMap childrenData, Object children, BMap rootNode, String keyValue, - Type fieldType, FieldDetails fieldDetails, boolean nilValue) throws Exception { + Type fieldType, FieldDetails fieldDetails, boolean nilValue) + throws Exception { if (childrenData.size() > 0) { if (children instanceof BMap) { BMap data = (BMap) children; @@ -653,7 +654,7 @@ private static Object validateResult(Object result, BString elementName) { Object validateResult; if (result == null) { validateResult = fromString(EMPTY_STRING); - } else if (result instanceof BMap && ((BMap) result).get(elementName) != null) { + } else if (result instanceof BMap && ((BMap) result).containsKey(elementName)) { validateResult = ((BMap) result).get(elementName); } else { validateResult = result; From 47a21c007d6f21436b02871fb423087b64864b00 Mon Sep 17 00:00:00 2001 From: Danesh Kuruppu Date: Wed, 11 Dec 2024 19:39:09 +0530 Subject: [PATCH 3/3] update the change logs --- changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/changelog.md b/changelog.md index fc67aaf0..9b739b2e 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed +- [Add support for xsi:nil Attribute in xml to json/record conversion](https://github.com/ballerina-platform/ballerina-library/issues/7462) + +## [2.6.2] - 2023-08-17 + ### Fixed [Fix the mismatch error by `fromXml` API while the field has the name annotation](https://github.com/ballerina-platform/ballerina-standard-library/issues/3802)