Skip to content

Commit

Permalink
Merge pull request #418 from ballerina-platform/task
Browse files Browse the repository at this point in the history
Remove some limitation from the output of toRecord()
  • Loading branch information
kalaiyarasiganeshalingam authored Apr 18, 2022
2 parents 4859062 + 1f2c6c3 commit e459ee7
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 124 deletions.
4 changes: 2 additions & 2 deletions ballerina/Ballerina.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
org = "ballerina"
name = "xmldata"
version = "2.2.1"
version = "2.2.2"
authors = ["Ballerina"]
keywords = ["xml", "json"]
repository = "https://github.com/ballerina-platform/module-ballerina-xmldata"
Expand All @@ -10,4 +10,4 @@ license = ["Apache-2.0"]
distribution = "2201.0.1"

[[platform.java11.dependency]]
path = "../native/build/libs/xmldata-native-2.2.1.jar"
path = "../native/build/libs/xmldata-native-2.2.2-SNAPSHOT.jar"
2 changes: 1 addition & 1 deletion ballerina/Dependencies.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ modules = [
[[package]]
org = "ballerina"
name = "xmldata"
version = "2.2.1"
version = "2.2.2"
dependencies = [
{org = "ballerina", name = "jballerina.java"},
{org = "ballerina", name = "test"}
Expand Down
84 changes: 84 additions & 0 deletions ballerina/tests/xml_from_json_test.bal
Original file line number Diff line number Diff line change
Expand Up @@ -769,3 +769,87 @@ isolated function testWithAttribute3() {
test:assertFail("failed to convert json to xml");
}
}

type Order record {
Invoice Invoice;
};

type Invoice record {
PurchesedItems PurchesedItems;
Address1 Address;
string _xmlns?;
string _xmlns\:ns?;
string _attr?;
string _ns\:attr?;
};

type PurchesedItems record {
Purchase[] PLine;
};

type Purchase record {
string|ItemCode ItemCode;
int Count;
};

type ItemCode record {
string _discount;
string \#content;
};

type Address1 record {
string StreetAddress;
string City;
int Zip;
string Country;
string _xmlns?;
};

@test:Config {
groups: ["fromJson"]
}
isolated function testfromjsonwithRecord() {
Order data = {
Invoice: {
PurchesedItems: {
PLine: [
{ItemCode: "223345", Count: 10},
{ItemCode: "223300", Count: 7},
{
ItemCode: {_discount: "22%", \#content: "200777"},
Count: 7
}
]
},
Address: {
StreetAddress: "20, Palm grove, Colombo 3",
City: "Colombo",
Zip: 300,
Country: "LK"
},
_attr: "attr-val",
_xmlns: "example.com",
_xmlns\:ns: "ns.com"
}
};
string expected = "<Invoice xmlns=\"example.com\" xmlns:ns=\"ns.com\" attr=\"attr-val\">" +
"<PurchesedItems>" +
"<PLine><ItemCode>223345</ItemCode><Count>10</Count></PLine>" +
"<PLine><ItemCode>223300</ItemCode><Count>7</Count></PLine>" +
"<PLine><ItemCode discount=\"22%\">200777</ItemCode><Count>7</Count></PLine>" +
"</PurchesedItems>" +
"<Address>" +
"<StreetAddress>20, Palm grove, Colombo 3</StreetAddress>" +
"<City>Colombo</City>" +
"<Zip>300</Zip>" +
"<Country>LK</Country>" +
"</Address>" +
"</Invoice>";
json jsonData = data.toJson();
xml?|error result = fromJson(jsonData, {attributePrefix: "_"});
if result is xml {
test:assertEquals(result.toString(), expected.toString());
} else {
test:assertFail("failed to convert json to xml");
}
}
54 changes: 10 additions & 44 deletions ballerina/tests/xml_to_record_test.bal
Original file line number Diff line number Diff line change
Expand Up @@ -52,40 +52,6 @@ isolated function testToRecordWithEscapedString() returns error? {
test:assertEquals(actual, expected, msg = "testToRecordWithEscapedString result incorrect");
}

type Order record {
Invoice Invoice;
};

type Invoice record {
PurchesedItems PurchesedItems;
Address1 Address;
string _xmlns?;
string _xmlns_ns?;
string _attr?;
string _ns_attr?;
};

type PurchesedItems record {
Purchase[] PLine;
};

type Purchase record {
string|ItemCode ItemCode;
int Count;
};

type ItemCode record {
string _discount;
};

type Address1 record {
string StreetAddress;
string City;
int Zip;
string Country;
string _xmlns?;
};

xml e2 = xml `<Invoice xmlns="example.com" attr="attr-val" xmlns:ns="ns.com" ns:attr="ns-attr-val">
<PurchesedItems>
<PLine><ItemCode>223345</ItemCode><Count>10</Count></PLine>
Expand All @@ -111,7 +77,7 @@ function testToRecordComplexXmlElement() returns error? {
{ItemCode: "223345", Count: 10},
{ItemCode: "223300", Count: 7},
{
ItemCode: {"_discount": "22%", "#content": "200777"},
ItemCode: {_discount: "22%", \#content: "200777"},
Count: 7
}
]
Expand All @@ -124,9 +90,9 @@ function testToRecordComplexXmlElement() returns error? {
_xmlns: ""
},
_xmlns: "example.com",
_xmlns_ns: "ns.com",
_xmlns\:ns: "ns.com",
_attr: "attr-val",
_ns_attr: "ns-attr-val"
_ns\:attr: "ns-attr-val"
}
};
Order actual = check toRecord(e2);
Expand All @@ -144,7 +110,7 @@ function testToRecordComplexXmlElementWithoutPreserveNamespaces() returns error?
{ItemCode: "223345", Count: 10},
{ItemCode: "223300", Count: 7},
{
ItemCode: {"_discount": "22%", "#content": "200777"},
ItemCode: {_discount: "22%", \#content: "200777"},
Count: 7
}
]
Expand Down Expand Up @@ -264,8 +230,8 @@ type r2 record {
};

type Root2 record {
string _xmlns_ns;
string _ns_x;
string _xmlns\:ns;
string _ns\:x;
string _x;
};

Expand All @@ -277,8 +243,8 @@ isolated function testToRecordWithMultipleAttributesAndNamespaces() returns Erro

r2 expected = {
Root: {
_xmlns_ns: "ns.com",
_ns_x: "y",
_xmlns\:ns: "ns.com",
_ns\:x: "y",
_x: "z"
}
};
Expand Down Expand Up @@ -476,7 +442,7 @@ type BookStore2 record {
Address3 address;
Codes codes;
string _status;
string _xmlns_ns0;
string _xmlns\:ns0;
};

@test:Config {
Expand Down Expand Up @@ -515,7 +481,7 @@ isolated function testToRecordWithNamespaces() returns error? {
codes: {
item: [4, 8, 9]
},
_xmlns_ns0: "http://sample.com/test",
_xmlns\:ns0: "http://sample.com/test",
_status: "online"
}
};
Expand Down
64 changes: 36 additions & 28 deletions ballerina/xmldata.bal
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ public type JsonOptions record {
# successfully converted or else an `xmldata:Error`
public isolated function fromJson(json jsonValue, JsonOptions options = {}) returns xml?|Error {
if !isSingleNode(jsonValue) {
return getElement("root", check traverseNode(jsonValue, {}, options),
check getAttributesMap(jsonValue, options = options));
return getElement("root", check traverseNode(jsonValue, {}, options), options,
check getAttributesMap(jsonValue, options));
} else {
map<json>|error jMap = jsonValue.ensureType();
if jMap is map<json> {
Expand All @@ -54,15 +54,15 @@ public isolated function fromJson(json jsonValue, JsonOptions options = {}) retu
}
json value = jMap.toArray()[0];
if value is json[] {
return getElement("root", check traverseNode(value, {}, options, jMap.keys()[0]),
check getAttributesMap(value, options = options));
return getElement("root", check traverseNode(value, {}, options, jMap.keys()[0]), options,
check getAttributesMap(value, options));
} else {
string key = jMap.keys()[0];
if key == CONTENT {
return xml:createText(value.toString());
}
return getElement(jMap.keys()[0], check traverseNode(value, {}, options),
check getAttributesMap(value, options = options));
return getElement(jMap.keys()[0], check traverseNode(value, {}, options), options,
check getAttributesMap(value, options));
}
}
if jsonValue !is null {
Expand All @@ -73,23 +73,22 @@ public isolated function fromJson(json jsonValue, JsonOptions options = {}) retu
}
}

isolated function traverseNode(json jNode, map<string> parentNamespaces, JsonOptions options = {},
isolated function traverseNode(json jNode, map<string> parentNamespaces, JsonOptions options,
string? key = ()) returns xml|Error {
string arrayEntryTag = options.arrayEntryTag == "" ? "item" : options.arrayEntryTag;
string attributePrefix = options.attributePrefix == "" ? "@" : options.attributePrefix;
xml xNode = xml ``;
if jNode is map<json> {
foreach [string, json] [k, v] in jNode.entries() {
if !k.startsWith(attributePrefix) {
if !k.startsWith(options.attributePrefix) {
if k == CONTENT {
xNode += xml:createText(v.toString());
} else if v is json[] {
xml node = check traverseNode(v, check getNamespacesMap(v, parentNamespaces, options), options, k);
xml node = check traverseNode(v, check getNamespacesMap(v, options, parentNamespaces), options, k);
xNode += node;
} else {
xml node = check getElement(k, check traverseNode(v,
check getNamespacesMap(v, parentNamespaces, options), options),
check getAttributesMap(v, parentNamespaces, options = options));
check getNamespacesMap(v, options, parentNamespaces), options),
options,
check getAttributesMap(v, options, parentNamespaces));
xNode += node;
}
}
Expand All @@ -100,11 +99,12 @@ isolated function traverseNode(json jNode, map<string> parentNamespaces, JsonOpt
if (key is string) {
arrayEntryTagKey = key;
} else {
arrayEntryTagKey = arrayEntryTag;
arrayEntryTagKey = options.arrayEntryTag;
}
xml item = check getElement(arrayEntryTagKey, check traverseNode(i,
check getNamespacesMap(i, parentNamespaces, options)),
check getAttributesMap(i, parentNamespaces, options = options));
check getNamespacesMap(i, options, parentNamespaces), options),
options,
check getAttributesMap(i, options, parentNamespaces));
xNode += item;
}
} else {
Expand All @@ -124,9 +124,9 @@ isolated function isSingleNode(json node) returns boolean {
return true;
}

isolated function getElement(string name, xml children, map<string> attributes = {}, JsonOptions options = {})
isolated function getElement(string name, xml children, JsonOptions options, map<string> attributes = {})
returns xml|Error {
string attributePrefix = options.attributePrefix == "" ? "@" : options.attributePrefix;
string attributePrefix = options.attributePrefix;
xml:Element element;
int? index = name.indexOf(":");
if index is int {
Expand All @@ -149,9 +149,9 @@ isolated function getElement(string name, xml children, map<string> attributes =
return element;
}

isolated function getAttributesMap(json jTree, map<string> parentNamespaces = {}, JsonOptions options = {})
isolated function getAttributesMap(json jTree, JsonOptions options, map<string> parentNamespaces = {})
returns map<string>|Error {
string attributePrefix = options.attributePrefix == "" ? "@" : options.attributePrefix;
string attributePrefix = options.attributePrefix;
map<string> attributes = parentNamespaces.clone();
map<json>|error attr = jTree.ensureType();
if attr is map<json> {
Expand All @@ -160,9 +160,12 @@ isolated function getAttributesMap(json jTree, map<string> parentNamespaces = {}
if v is map<json> || v is json[] {
return error Error("attribute cannot be an object or array");
}
if k.startsWith(attributePrefix + "xmlns") {
string prefix = k.substring(<int>k.indexOf(":") + 1);
attributes[string `{${XMLNS_NAMESPACE_URI}}${prefix}`] = v.toString();
int? index = k.indexOf(":");
if index is int {
if k.startsWith(attributePrefix + "xmlns") {
string prefix = k.substring(index + 1);
attributes[string `{${XMLNS_NAMESPACE_URI}}${prefix}`] = v.toString();
}
} else {
attributes[k.substring(1)] = v.toString();
}
Expand All @@ -172,20 +175,25 @@ isolated function getAttributesMap(json jTree, map<string> parentNamespaces = {}
return attributes;
}

isolated function getNamespacesMap(json jTree, map<string> parentNamespaces = {}, JsonOptions options = {})
isolated function getNamespacesMap(json jTree, JsonOptions options, map<string> parentNamespaces = {})
returns map<string>|Error {
string attributePrefix = options.attributePrefix == "" ? "@" : options.attributePrefix;
string attributePrefix = options.attributePrefix;
map<string> namespaces = parentNamespaces.clone();
map<json>|error attr = jTree.ensureType();
if attr is map<json> {
foreach [string, json] [k, v] in attr.entries() {
if k.startsWith(attributePrefix) {
if v is map<json> || v is json[] {
return error Error("attribute cannot be an object or array");
return error Error("attribute cannot be an object or array.");
}
if k.startsWith(attributePrefix + "xmlns") {
string prefix = k.substring(<int>k.indexOf(":") + 1);
namespaces[string `{${XMLNS_NAMESPACE_URI}}${prefix}`] = v.toString();
int? index = k.indexOf(":");
if index is int {
string prefix = k.substring(index + 1);
namespaces[string `{${XMLNS_NAMESPACE_URI}}${prefix}`] = v.toString();
} else {
namespaces[string `{${XMLNS_NAMESPACE_URI}}`] = v.toString();
}
}
}
}
Expand Down
9 changes: 8 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Added
- [[2406] Add `toRecord` function which converts an XML to a Record](https://github.com/ballerina-platform/ballerina-standard-library/issues/2406)

### Fixed
- [Fix the limitations of using the colon in the output of the `toRecord`](https://github.com/ballerina-platform/module-ballerina-xmldata/pull/418)

## [2.1.0] - 2021-12-13

### Added
- [Add `toRecord` function which converts an XML to a Record](https://github.com/ballerina-platform/ballerina-standard-library/issues/2406)

## [1.1.0-alpha6] - 2021-04-02

Expand Down
Loading

0 comments on commit e459ee7

Please sign in to comment.