Leonidas-from-XIV / node-xml2js

XML to JavaScript object converter.
MIT License
4.9k stars 606 forks source link

Building XML from object with undefined properties crashes #151

Open gabmontes opened 10 years ago

gabmontes commented 10 years ago

@Leonidas-from-XIV if you try to build an XML out of an object containing undefined properties, the program crashes.

For instance, the following code will fail:

var xml2js = require('xml2js');

var obj = {name: "Super", Surname: "Man", age: undefined};

var builder = new xml2js.Builder();
var xml = builder.buildObject(obj); // crashes with TypeError: Cannot call method 'toString' of undefined

On the other hand, if the age property is null, the builder works fine.

Is there a rationale behind the difference?

Leonidas-from-XIV commented 10 years ago

No, I think it is just an artifact of the implementation, which doesn't check for undefined. In any case, it is kinda unclear what the XML representation of undefined would even be. "undefined"? That would get read as "undefined" and so the translation is not really transparent (a property that I think is kinda neat).

gabmontes commented 10 years ago

@Leonidas-from-XIV thanks for your quick reply!

Any alternative chosen should let us go JSON > XML > JSON and get an equivalent object in the end. Much the same as JSON.parse and JSON.stringify. Then, a property set to undefined should not be translated to the XML. What do you think?

IMHO, what should not happen is the library to fail if a valid JSON is given to the XML builder, no matter what the final decision is in regards to the values that do not translate well to an XML as undefined or perhaps null.

Leonidas-from-XIV commented 10 years ago

Yes, I think it would be better to bail on invalid input instead of generating XML that might be ok, but is mysteriously missing some items, because they were accidentally set to undefined or something.

jcsahnwaldt commented 6 years ago

To some extent, this has been fixed, but some issues remain.

const xml2js = require('xml2js');

function test(js) {
  let builder = new xml2js.Builder({
    headless: true
  });
  console.dir(js, {depth: null});
  try {
    let xml = builder.buildObject(js);
    console.log(xml);
  }
  catch (err) {
    console.log(err.stack.split('\n', 5).join('\n')+'\n    ...');
  }
  console.log();
}

test({root: ''});
test({root: null});
test({root: undefined});

test({name: "Super", age: ''});
test({name: "Super", age: null});
test({name: "Super", age: undefined});

test({name: "Super", age: ['']});
test({name: "Super", age: [null]});
test({name: "Super", age: [undefined]});

test({root: {name: "Super", age: ''}});
test({root: {name: "Super", age: null}});
test({root: {name: "Super", age: undefined}});

test({root: {name: "Super", age: ['']}});
test({root: {name: "Super", age: [null]}});
test({root: {name: "Super", age: [undefined]}});

test({root: {$: {foo: ''}}});
test({root: {$: {foo: null}}});
test({root: {$: {foo: undefined}}});

test({root: {child: {$: {foo: ''}}}});
test({root: {child: {$: {foo: null}}}});
test({root: {child: {$: {foo: undefined}}}});

Output (paths in stack traces sanitized):

{ root: '' }
<root/>

{ root: null }
<root/>

{ root: undefined }
Error: Missing element text
    at new XMLText (./node_modules/xmlbuilder/lib/XMLText.js:15:15)
    at XMLElement.module.exports.XMLNode.text (./node_modules/xmlbuilder/lib/XMLNode.js:165:15)
    at XMLElement.module.exports.XMLNode.txt (./node_modules/xmlbuilder/lib/XMLNode.js:365:19)
    at ./node_modules/xml2js/lib/builder.js:57:23
    ...

{ name: 'Super', age: '' }
<root>
  <name>Super</name>
  <age/>
</root>

{ name: 'Super', age: null }
<root>
  <name>Super</name>
  <age/>
</root>

{ name: 'Super', age: undefined }
<root>
  <name>Super</name>
  <age/>
</root>

{ name: 'Super', age: [ '' ] }
<root>
  <name>Super</name>
  <age/>
</root>

{ name: 'Super', age: [ null ] }
<root>
  <name>Super</name>
  <age/>
</root>

{ name: 'Super', age: [ undefined ] }
Error: Missing element text
    at new XMLText (./node_modules/xmlbuilder/lib/XMLText.js:15:15)
    at XMLElement.module.exports.XMLNode.text (./node_modules/xmlbuilder/lib/XMLNode.js:165:15)
    at XMLElement.module.exports.XMLNode.txt (./node_modules/xmlbuilder/lib/XMLNode.js:365:19)
    at ./node_modules/xml2js/lib/builder.js:57:23
    ...

{ root: { name: 'Super', age: '' } }
<root>
  <name>Super</name>
  <age/>
</root>

{ root: { name: 'Super', age: null } }
<root>
  <name>Super</name>
  <age/>
</root>

{ root: { name: 'Super', age: undefined } }
<root>
  <name>Super</name>
  <age/>
</root>

{ root: { name: 'Super', age: [ '' ] } }
<root>
  <name>Super</name>
  <age/>
</root>

{ root: { name: 'Super', age: [ null ] } }
<root>
  <name>Super</name>
  <age/>
</root>

{ root: { name: 'Super', age: [ undefined ] } }
Error: Missing element text
    at new XMLText (./node_modules/xmlbuilder/lib/XMLText.js:15:15)
    at XMLElement.module.exports.XMLNode.text (./node_modules/xmlbuilder/lib/XMLNode.js:165:15)
    at XMLElement.module.exports.XMLNode.txt (./node_modules/xmlbuilder/lib/XMLNode.js:365:19)
    at ./node_modules/xml2js/lib/builder.js:57:23
    ...

{ root: { '$': { foo: '' } } }
<root foo=""/>

{ root: { '$': { foo: null } } }
Error: Missing attribute value for attribute foo of element root
    at new XMLAttribute (./node_modules/xmlbuilder/lib/XMLAttribute.js:13:15)
    at XMLElement.module.exports.XMLElement.attribute (./node_modules/xmlbuilder/lib/XMLElement.js:72:35)
    at XMLElement.module.exports.XMLElement.att (./node_modules/xmlbuilder/lib/XMLElement.js:100:19)
    at ./node_modules/xml2js/lib/builder.js:76:39
    ...

{ root: { '$': { foo: undefined } } }
Error: Missing attribute value for attribute foo of element root
    at new XMLAttribute (./node_modules/xmlbuilder/lib/XMLAttribute.js:13:15)
    at XMLElement.module.exports.XMLElement.attribute (./node_modules/xmlbuilder/lib/XMLElement.js:72:35)
    at XMLElement.module.exports.XMLElement.att (./node_modules/xmlbuilder/lib/XMLElement.js:100:19)
    at ./node_modules/xml2js/lib/builder.js:76:39
    ...

{ root: { child: { '$': { foo: '' } } } }
<root>
  <child foo=""/>
</root>

{ root: { child: { '$': { foo: null } } } }
Error: Missing attribute value for attribute foo of element child
    at new XMLAttribute (./node_modules/xmlbuilder/lib/XMLAttribute.js:13:15)
    at XMLElement.module.exports.XMLElement.attribute (./node_modules/xmlbuilder/lib/XMLElement.js:72:35)
    at XMLElement.module.exports.XMLElement.att (./node_modules/xmlbuilder/lib/XMLElement.js:100:19)
    at ./node_modules/xml2js/lib/builder.js:76:39
    ...

{ root: { child: { '$': { foo: undefined } } } }
Error: Missing attribute value for attribute foo of element child
    at new XMLAttribute (./node_modules/xmlbuilder/lib/XMLAttribute.js:13:15)
    at XMLElement.module.exports.XMLElement.attribute (./node_modules/xmlbuilder/lib/XMLElement.js:72:35)
    at XMLElement.module.exports.XMLElement.att (./node_modules/xmlbuilder/lib/XMLElement.js:100:19)
    at ./node_modules/xml2js/lib/builder.js:76:39
    ...