Skip to content

Commit

Permalink
Fix for null and undefined attributes when building xml (#585) (#598)
Browse files Browse the repository at this point in the history
* fix (xmlbuilder): null and undefined attributes are suppressed in the built xml (#585)

* test (xmlbuilder): add tests on null and undefined attributes building (#585)

Null and Undefined attributes should be suppressed when ignoreAttributes is false
both when format is true and when it is false
  • Loading branch information
cecia234 committed Jul 30, 2023
1 parent a04cd1e commit df7008f
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 4 deletions.
113 changes: 113 additions & 0 deletions spec/j2x_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -483,4 +483,117 @@ describe("XMLBuilder", function() {
expect(result).toEqual(expected);
});

it("should suppress null attributes in the xml when format is true and ignoreAttributes is false", function () {
const jObj = {
"list": {
"item": [
"one",
{ "#text": "two" },
{ "#text": "three", "@_attr": null }, // only one null attr
{ "#text": "four", "@_attr": "foo", "@_attr1": null }, // one defined attr and one null attr
{ "#text": "five", "@_attr": null, "@_attr1": "baz" }, // one null attr and one defined attr
{ "#text": "six", "@_attr": "foo", "@_attr1": "baz" }, // all defined attrs (more than one)
{ "#text": "seven", "@_attr": null, "@_attr1": null }, // all null attrs (more than one)
]
}
};
const builder = new XMLBuilder({
ignoreAttributes: false,
format: true
});
const result = builder.build(jObj);
const expected = `
<list>
<item>one</item>
<item>two</item>
<item>three</item>
<item attr="foo">four</item>
<item attr1="baz">five</item>
<item attr="foo" attr1="baz">six</item>
<item>seven</item>
</list>`;

expect(result.replace(/\s+/g, "")).toEqual(expected.replace(/\s+/g, ""));
});

it("should suppress null attributes in the xml when format is false and ignoreAttributes is false", function () {
const jObj = {
"list": {
"item": [
"one",
{ "#text": "two" },
{ "#text": "three", "@_attr": null }, // only one null attr
{ "#text": "four", "@_attr": "foo", "@_attr1": null }, // one defined attr and one null attr
{ "#text": "five", "@_attr": null, "@_attr1": "baz" }, // one null attr and one defined attr
{ "#text": "six", "@_attr": "foo", "@_attr1": "baz" }, // all defined attrs (more than one)
{ "#text": "seven", "@_attr": null, "@_attr1": null }, // all null attrs (more than one)
]
}
};
const builder = new XMLBuilder({
ignoreAttributes: false,
format: false
});
const result = builder.build(jObj);
const expected = `<list><item>one</item><item>two</item><item>three</item><item attr="foo">four</item><item attr1="baz">five</item><item attr="foo" attr1="baz">six</item><item>seven</item></list>`;

expect(result).toEqual(expected);
});

it("should suppress undefined attributes in the xml when format is true and ignoreAttributes is false", function () {
const jObj = {
"list": {
"item": [
"one",
{ "#text": "two" },
{ "#text": "three", "@_attr": undefined }, // only one undefined attr
{ "#text": "four", "@_attr": "foo", "@_attr1": undefined }, // one defined attr and one undefined attr
{ "#text": "five", "@_attr": undefined, "@_attr1": "baz" }, // one undefined attr and one defined attr
{ "#text": "six", "@_attr": "foo", "@_attr1": "baz" }, // all defined attrs (more than one)
{ "#text": "seven", "@_attr": undefined, "@_attr1": undefined }, // all undefined attrs (more than one)
]
}
};
const builder = new XMLBuilder({
ignoreAttributes: false,
format: true
});
const result = builder.build(jObj);
const expected = `
<list>
<item>one</item>
<item>two</item>
<item>three</item>
<item attr="foo">four</item>
<item attr1="baz">five</item>
<item attr="foo" attr1="baz">six</item>
<item>seven</item>
</list>`;

expect(result.replace(/\s+/g, "")).toEqual(expected.replace(/\s+/g, ""));
});

it("should suppress undefined attributes in the xml when format is false and ignoreAttributes is false", function () {
const jObj = {
"list": {
"item": [
"one",
{ "#text": "two" },
{ "#text": "three", "@_attr": undefined }, // only one undefined attr
{ "#text": "four", "@_attr": "foo", "@_attr1": undefined }, // one defined attr and one undefined attr
{ "#text": "five", "@_attr": undefined, "@_attr1": "baz" }, // one undefined attr and one defined attr
{ "#text": "six", "@_attr": "foo", "@_attr1": "baz" }, // all defined attrs (more than one)
{ "#text": "seven", "@_attr": undefined, "@_attr1": undefined }, // all undefined attrs (more than one)
]
}
};
const builder = new XMLBuilder({
ignoreAttributes: false,
format: false
});
const result = builder.build(jObj);
const expected = `<list><item>one</item><item>two</item><item>three</item><item attr="foo">four</item><item attr1="baz">five</item><item attr="foo" attr1="baz">six</item><item>seven</item></list>`;

expect(result).toEqual(expected);
});
});
18 changes: 14 additions & 4 deletions src/xmlbuilder/json2xml.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,19 @@ Builder.prototype.j2x = function(jObj, level) {
let val = '';
for (let key in jObj) {
if (typeof jObj[key] === 'undefined') {
// supress undefined node
// supress undefined node only if it is not an attribute
if (this.isAttribute(key)) {
val += '';
}
} else if (jObj[key] === null) {
if(key[0] === "?") val += this.indentate(level) + '<' + key + '?' + this.tagEndChar;
else val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
// null attribute should be ignored by the attribute list, but should not cause the tag closing
if (this.isAttribute(key)) {
val += '';
} else if (key[0] === '?') {
val += this.indentate(level) + '<' + key + '?' + this.tagEndChar;
} else {
val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
}
// val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
} else if (jObj[key] instanceof Date) {
val += this.buildTextValNode(jObj[key], key, '', level);
Expand Down Expand Up @@ -176,7 +185,8 @@ Builder.prototype.buildObjectNode = function(val, key, attrStr, level) {
tagEndExp = "";
}

if (attrStr && val.indexOf('<') === -1) {
// attrStr is an empty string in case the attribute came as undefined or null
if ((attrStr || attrStr === '') && val.indexOf('<') === -1) {
return ( this.indentate(level) + '<' + key + attrStr + piClosingChar + '>' + val + tagEndExp );
} else if (this.options.commentPropName !== false && key === this.options.commentPropName && piClosingChar.length === 0) {
return this.indentate(level) + `<!--${val}-->` + this.newLine;
Expand Down

0 comments on commit df7008f

Please sign in to comment.