Leonidas-from-XIV / node-xml2js

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

How to insert an element in specific order? #291

Open hezjing opened 8 years ago

hezjing commented 8 years ago

The following code snippet will parse a string and then add a header element in the body

var parseString = require('xml2js').parseString;
var xml = "<root><body>Hello xml2js!</body></root>"
parseString(xml, function (err, result) {
    result.header = {$: {background: 'black'}};
    var builder = new xml2js.Builder();
    var xml = builder.buildObject(result);
    console.dir(xml);
});

and the output is

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <body>Hello xml2js!</body>
    <header background="black"/>
</root>

How can I order the elements so that the header appears before the body?

rolljee commented 8 years ago

Any update about this @hezjing ? I am dealing with this too..

jcsahnwaldt commented 6 years ago

Actually, xml2js can hardly make any promises about element order, because JavaScript itself is very lax in this regard. But even though there are few guarantees, JavaScript engines tend to iterate properties in the order they were inserted.

In this case, you could create a new object, insert header: {$: {background: 'black'}} first, and the other properties later:

const xml2js = require('xml2js');

let parser = new xml2js.Parser();
let builder = new xml2js.Builder();

let xml =
`<root>
  <body>Hello xml2js!</body>
</root>`;

parser.parseString(xml, function (err, res) {
  console.dir(res, {depth: null});
  let obj = {
    root: {
      header: {$: {background: 'black'}},
      ...res.root
    }
  };
  console.dir(obj, {depth: null});
  let xml = builder.buildObject(obj);
  console.log(xml);
});

The syntax is tricky though. Took me three attempts to get it right... I'm using object spread syntax, which is supported in Node.js since 8.3.0.

Output (on my machine, may be different in other environments):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
  <header background="black"/>
  <body>Hello xml2js!</body>
</root>

If we want to be able to handle different root element tags, we can avoid hard-coding root and make the code more generic. The syntax gets even more tricky though...

const xml2js = require('xml2js');

let parser = new xml2js.Parser();
let builder = new xml2js.Builder();

let xml =
`<root>
  <body>Hello xml2js!</body>
</root>`;

parser.parseString(xml, function (err, res) {
  console.dir(res, {depth: null});
  let key = Object.keys(res)[0];
  let obj = {
    [key]: {
      header: {$: {background: 'black'}},
      ...res[key]
    }
  };
  console.dir(obj, {depth: null});
  let xml = builder.buildObject(obj);
  console.log(xml);
});

Output: same as above.

jcsahnwaldt commented 6 years ago

I think this can be closed. There's probably not much else xml2js can do here.