Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parse XML to JSON and back ... with xml2js and xmlbuilder?

Trying to parse XML into JSON with xml2js and then return the JSON to XML using xmlbuilder (usually after modifying the content programmatically).

I think that the two should be complements, per this post https://github.com/oozcitak/xmlbuilder-js/issues/69. But am having some difficulty, and it's gotta be that I'm not getting the config parameters right.

Here's the code I'm running:

var xml = fs.readFileSync(__dirname + '/../xml/theme.xml', 'utf8');

xml2js.parseString(xml, { attrkey: '@',  xmlns: true }, function(err, json) {
    var xml2 = xmlbuilder.create(json,
       {version: '1.0', encoding: 'UTF-8', standalone: true}
    ).end({pretty: true, standalone: true})
});

Here's the first bit of the original XML:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme">
    <a:themeElements>
        <a:clrScheme name="Office">
            <a:dk1>
                <a:sysClr val="windowText" lastClr="000000"/>
            </a:dk1>
            <a:lt1>
                <a:sysClr val="window" lastClr="FFFFFF"/>
            </a:lt1>
            <a:dk2>
                <a:srgbClr val="1F497D"/>
            </a:dk2>
            ...
     </a:themeElements>           
 </a:theme>

Here;s how xml2js parses that to JSON, this looks right to me:

{
    "a:theme": {
        "@": {
            "xmlns:a": {
                "name": "xmlns:a",
                "value": "http://schemas.openxmlformats.org/drawingml/2006/main",
                "prefix": "xmlns",
                "local": "a",
                "uri": "http://www.w3.org/2000/xmlns/"
            },
            "name": {
                "name": "name",
                "value": "Office Theme",
                "prefix": "",
                "local": "name",
                "uri": ""
            }
        },
        "@ns": {
            "uri": "http://schemas.openxmlformats.org/drawingml/2006/main",
            "local": "theme"
        },
        "a:themeElements": [
            {
                "@ns": {
                    "uri": "http://schemas.openxmlformats.org/drawingml/2006/main",
                    "local": "themeElements"
                },
                "a:clrScheme": [
                    {
                        "@": {
                            "name": {
                                "name": "name",
                                "value": "Office",
                                "prefix": "",
                                "local": "name",
                                "uri": ""
                            }
                        },
                        "@ns": {
                            "uri": "http://schemas.openxmlformats.org/drawingml/2006/main",
                            "local": "clrScheme"
                        },
                       ...

Note that in the JSON above:

  1. the attribute (e.g. name=) are turned into keys inside an @ object and
  2. the attribute values are turned into objects

Now here's how it looks when xmlbuilder turns it back into XML:

<a:theme ="[object Object]" ns="[object Object]">
  <a:themeElements ns="[object Object]">
    <a:clrScheme ="[object Object]" ns="[object Object]">
      <a:dk1 ns="[object Object]">
        <a:sysClr ="[object Object]" ns="[object Object]"/>
      </a:dk1>
      <a:lt1 ns="[object Object]">
        <a:sysClr ="[object Object]" ns="[object Object]"/>
      </a:lt1>
       ...
    </a:themeElements>
 </a:theme>

So there are two problems that XML builder is facing: * it's not recognizing the attribute names within the @ object and * it's not recognizing the attribute value within the attribute object

Hacking it appears that xmlbuilder wants attributes names structured like:

  `{ "@name": "Office Theme"} `

rather than

  `{ "@" : { "name" : { value: "Office Theme" }}}`

Should I configure xml2js differently, xmlbuilder differently, or is there a different pair of libraries that can parse XML -> JSON -> XML?

like image 926
prototype Avatar asked Feb 24 '15 05:02

prototype


People also ask

How do I convert XML to JSON?

To convert an XML document to JSON, follow these steps: Select the XML to JSON action from the Tools > JSON Tools menu. Choose or enter the Input URL of the XML document. Choose the path of the Output file that will contain the resulting JSON document.

Can we convert XML to JSON in JavaScript?

fromStr('string with xml') method in your JavaScript script. It returns the JSON object. If you want to convert the XML to JSON String, add a second argument with the value of "string".

How do you access XML elements in node JS?

Node. js has no inbuilt library to read XML. The popular module xml2js can be used to read XML. The installed module exposes the Parser object, which is then used to read XML.


3 Answers

The xml2js package comes with its own XML builder, to which the documentation has to say:

Since 0.4.0, objects can be also be used to build XML:

var fs = require('fs'),
    xml2js = require('xml2js');

var obj = {name: "Super", Surname: "Man", age: 23};
var builder = new xml2js.Builder();
var xml = builder.buildObject(obj);

At the moment, a one to one bi-directional conversion is guaranteed only for default configuration, except for attrkey, charkey and explicitArray options you can redefine to your taste.

So, after dropping your custom parser configuration, this works perfectly for me:

var fs = require('fs');
var path = require('path');
var xml2js = require('xml2js');

xmlFileToJs('theme.xml', function (err, obj) {
    if (err) throw (err);
    jsToXmlFile('theme2.xml', obj, function (err) {
        if (err) console.log(err);
    })
});
// -----------------------------------------------------------------------

function xmlFileToJs(filename, cb) {
    var filepath = path.normalize(path.join(__dirname, filename));
    fs.readFile(filepath, 'utf8', function (err, xmlStr) {
        if (err) throw (err);
        xml2js.parseString(xmlStr, {}, cb);
    });    
}

function jsToXmlFile(filename, obj, cb) {
    var filepath = path.normalize(path.join(__dirname, filename));
    var builder = new xml2js.Builder();
    var xml = builder.buildObject(obj);
    fs.writeFile(filepath, xml, cb);
}
like image 131
Tomalak Avatar answered Oct 10 '22 07:10

Tomalak


I came across similar requirement recently. Had to convert Yahoo weather service xml data into json object. Solved the issue with xml2js and js2xmlparser modules. Thought to share how I did here. An example which I worked out:

const https = require('https');
var parseString = require('xml2js').parseString;

https.get('https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22surat%22)&format=xml&env=store%3A%2F%2F    datatables.org%2Falltableswithkeys', (resp) => {
    let data = '';

    // A chunk of data has been recieved.
    resp.on('data', (chunk) => {
        data += chunk;
    });

    // The whole response has been received. Print out the result.
    resp.on('end', () => {
        //console.log(JSON.parse(data).explanation);

        parseString(data, function (err, result) {
            console.log(JSON.stringify(result));
        });
    });

}).on("error", (err) => {
    console.log("Error: " + err.message);
});

After this one can use js2xmlparser for converting the obtained json to xml.

js2xmlparser.parse("weather", data); 
....

here's a Github link for the detailed code: https://github.com/joshiparthin/ReNode/tree/master/soap-api

like image 1
Parth Joshi Avatar answered Oct 10 '22 07:10

Parth Joshi


While Converting XML to JSON , The xml2js maps the attributes to '$'. In case if your attributes key Name does not match with the Child Key Name . You could Merge the Attributes with Child elements . HEnce your JSON looks clean.

**

xml2js.Parser({ignoreAttrs : false, mergeAttrs : true})

**

Might Solve your problem.

like image 2
Sumanth Avatar answered Oct 10 '22 07:10

Sumanth