diff --git a/spec/j2x_spec.js b/spec/j2x_spec.js
index be0aa81e..2ab00f54 100644
--- a/spec/j2x_spec.js
+++ b/spec/j2x_spec.js
@@ -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 = `
+
+ - one
+ - two
+ - three
+ - four
+ - five
+ - six
+ - seven
+
`;
+
+ 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 = `- one
- two
- three
- four
- five
- six
- seven
`;
+
+ 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 = `
+
+ - one
+ - two
+ - three
+ - four
+ - five
+ - six
+ - seven
+
`;
+
+ 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 = `- one
- two
- three
- four
- five
- six
- seven
`;
+
+ expect(result).toEqual(expected);
+ });
});
diff --git a/src/xmlbuilder/json2xml.js b/src/xmlbuilder/json2xml.js
index 813043f3..9dae3472 100644
--- a/src/xmlbuilder/json2xml.js
+++ b/src/xmlbuilder/json2xml.js
@@ -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);
@@ -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) + `` + this.newLine;