javascriptxmlnode.jsparsinglibxml-js

How to get an object from an XML document?


When parsing documents using the excellent libxmljs library in Node.js, I stumbled across a case where a lot of nested elements were found, and the only thing I had to do was create a JS object from it.

Here is what the code looks like :

if (node.type() == 'element') {
        switch(node.name()) {
          case 'element1': {
              myObject.element1 = {}
              for (var i = 0; i < node.childNodes().length; i++) {
                  if(node.type() == 'element') {
                      switch(node.name()) {
                          case 'element2': {
                              myObject.element1.element2 = node.text()
                              ...
}}}}}}}} 
/* didn't count the number of closing brackets, but you get the idea ^_^ */

Is there a faster or built-in way to do such things, create an object from an XML string (or part of it) using libxmlJS ?

Note that, if it helps, the parsed XML must be validated against a XTD schema (which can really easily be done using this library)

Thanks


Solution

  • Here is some non-working code that can be found on this article :

    function XML2jsobj(node) {
    
        var data = {};
    
        // append a value
        function Add(name, value) {
            if (data[name]) {
                if (data[name].constructor != Array) {
                    data[name] = [data[name]];
                }
                data[name][data[name].length] = value;
            }
            else {
                data[name] = value;
            }
        };
    
        // element attributes
        var c, cn;
        for (c = 0; cn = node.attributes[c]; c++) {
            Add(cn.name, cn.value);
        }
    
        // child elements
        for (c = 0; cn = node.childNodes[c]; c++) {
            if (cn.nodeType == 1) {
                if (cn.childNodes.length == 1 && cn.firstChild.nodeType == 3) {
                    // text value
                    Add(cn.nodeName, cn.firstChild.nodeValue);
                }
                else {
                    // sub-object
                    Add(cn.nodeName, XML2jsobj(cn));
                }
            }
        }
    
        return data;
    
    }
    

    From that code, I could build something that seems to work with the latest libxmljs release, here it is :

    function XML2jsobj(node) {
        var data = {};
    
        // append a value
        function Add(name, value) {
            if (data[name]) {
                if (data[name].constructor != Array) {
                    data[name] = [data[name]];
                }
                data[name][data[name].length] = value;
            }
            else {
                data[name] = value;
            }
        };
    
        for (var c = 0; c < node.attrs().length; c++) {
            var cn = node.attrs()[c];
            Add(cn.name, cn.value);
        }
    
        // child elements
        for (var c = 0; c < node.childNodes().length; c++) {
            var cn = node.childNodes()[c];
            if (cn.type() == 'element') {
                if (cn.childNodes().length == 1 && cn.childNodes()[0].type() == 'text') {
                    // text value
                    Add(cn.name(), cn.childNodes()[0].text());
                }
                else {
                    // sub-object
                    Add(cn.name(), XML2jsobj(cn));
                }
            }
        }
    
        return data;
    }
    

    I hope this will have helped someone.