Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

First revision.

  • Loading branch information...
commit f4dba28cdd1cfef9f967b2011df9cf5f07ed8edd 0 parents
@SaltwaterC authored
Showing with 220 additions and 0 deletions.
  1. +11 −0 LICENSE.md
  2. +47 −0 README.md
  3. +145 −0 lib/libxml-to-js.js
  4. +17 −0 package.json
11 LICENSE.md
@@ -0,0 +1,11 @@
+# libxml-to-js license
+
+Copyright (c) 2011, Ștefan Rusu All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of Ștefan Rusu nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ȘTEFAN RUSU BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47 README.md
@@ -0,0 +1,47 @@
+## About
+
+This is a XML to JavaScript object parser. It uses the [libxmljs](https://github.com/polotek/libxmljs) module for the actual XML parsing. It aims to be an easy [xml2js](https://github.com/Leonidas-from-XIV/node-xml2js) replacement. I used xml2js for my own needs, but the error reporting of the underlying SAX parser is quite broken.
+
+It has a couple of implemented parsing modes. This is a side effect of the fact that at first I implemented the SAX parser, thereafter the string parser of libxmljs. The first has better error reporting while the last works asynchronously.
+
+## Installation
+
+Either manually clone this repository into your node_modules directory, or the recommended method:
+
+> npm install libxml-to-js
+
+## Usage mode
+
+### The string parser
+
+<pre>
+var parser = require('libxml-to-js').stringParser;
+var xml = 'xml string';
+
+parser(xml, function (error, result) {
+ if (error) {
+ console.error(error);
+ } else {
+ console.log(result);
+ }
+});
+</pre>
+
+### The SAX parser
+
+<pre>
+var parser = require('libxml-to-js').saxParser;
+var xml = 'xml string';
+
+parser(xml, function (error, result) {
+ if (error) {
+ console.error(error);
+ } else {
+ console.log(result);
+ }
+});
+</pre>
+
+## Notes
+
+The SAX parser usage is identical to the string parser usage. The only actual differece might be an error at the end of the XML string while the rest of the string being valid XML. In this case, the string parser returns the error argument while the SAX parser doesn't return the error, but succesfully return the XML contents. However, the string parser might be buggier if I messed up the recursion. Currently I didn't find anything that breaks it, but your mileage may vary.
145 lib/libxml-to-js.js
@@ -0,0 +1,145 @@
+var libxmljs = require('libxmljs');
+
+var libxml2js = function (obj, recurse) {
+ if ( ! recurse) {
+ obj = obj.root();
+ }
+
+ var jsobj = {}, children = obj.childNodes(), attributes = obj.attrs();
+
+ if (attributes.length > 0) {
+ jsobj['@'] = {};
+ for (var i = 0, atlen = attributes.length; i < atlen; i++) {
+ jsobj['@'][attributes[i].name()] = attributes[i].value();
+ }
+ }
+
+ for (var i = 0, chlen = children.length; i < chlen; i++) {
+ // <"text" kludge>
+ if (children[i].name() == 'text') {
+ jsobj['#'] = children[i].text().replace(/^\s*/, '').replace(/\s*$/, '');
+ if (jsobj['#'].match(/^\s*$/)) {
+ delete(jsobj['#']);
+ }
+ for (var j = 0, chtlen = children[i].childNodes().length; j < chtlen; j++) {
+ if (children[i].child(j).name() == 'text') {
+ var text = {}, textattrs = children[i].child(j).attrs();
+ text['#'] = children[i].child(j).text();
+ if (textattrs.length > 0) {
+ text['@'] = {};
+ }
+
+ for (var k = 0, atlen = textattrs.length; k < atlen; i++) {
+ text['@'][textattrs[k].name()] = textattrs[k].value();
+ }
+ jsobj['text'] = text;
+ break; // only allow one "<text></text>" element for now
+ }
+ }
+ continue;
+ }
+
+ // </"text" kludge>
+ if (typeof jsobj[children[i].name()] == 'undefined') {
+ if (children[i].childNodes().length == 1 && children[i].childNodes()[0].name() == 'text') {
+ jsobj[children[i].name()] = children[i].childNodes()[0].text();
+ } else {
+ jsobj[children[i].name()] = libxml2js(children[i], true);
+ }
+ } else {
+
+ if (typeof jsobj[children[i].name()] == 'string') {
+ var old = jsobj[children[i].name()];
+ jsobj[children[i].name()] = [];
+ jsobj[children[i].name()].push({'#': old});
+ } else if (typeof jsobj[children[i].name()] == 'object' && ! ('push' in jsobj[children[i].name()])) {
+ var old = jsobj[children[i].name()];
+ jsobj[children[i].name()] = [];
+ jsobj[children[i].name()].push(old);
+ }
+ jsobj[children[i].name()].push(libxml2js(children[i], true));
+ }
+ }
+
+ return jsobj;
+};
+
+exports.stringParser = function (xml, callback) {
+ try {
+ callback(undefined, libxml2js(libxmljs.parseXmlString(xml)));
+ } catch (err) {
+ callback(err, {});
+ }
+};
+
+exports.saxParser = function (xml, callback) {
+ var parser = new libxmljs.SaxParser(function (cb) {
+ var stack = [], end = false;
+
+ cb.onStartElementNS(function (elem, attrs) {
+ var obj = {};
+ obj['#'] = '';
+ obj['#name'] = elem;
+
+ if (attrs.length) {
+ obj['@'] = {};
+ var attrName, attrValue;
+
+ for (var i = 0; i < attrs.length; i++) {
+ attrName = attrs[i][0];
+ attrValue = attrs[i][3];
+ obj['@'][attrName] = attrValue;
+ }
+ }
+
+ stack.push(obj);
+ });
+
+ cb.onEndElementNS(function () {
+ var obj = stack.pop();
+ var nodeName = obj['#name'];
+ delete(obj['#name']);
+
+ var s = stack[stack.length - 1];
+ obj['#'] = obj['#'].replace(/^\s*/, '').replace(/\s*$/, '');
+ if (obj['#'].match(/^\s*$/)) {
+ delete(obj['#']);
+ } else {
+ if (Object.keys(obj).length === 1 && '#' in obj) {
+ obj = obj['#'];
+ }
+ }
+
+ if (stack.length > 0) {
+ var old;
+ if ( ! (nodeName in s)) {
+ return s[nodeName] = obj;
+ } else if (s[nodeName] instanceof Array) {
+ return s[nodeName].push(obj);
+ } else {
+ old = s[nodeName];
+ s[nodeName] = [old];
+ return s[nodeName].push(obj);
+ }
+ } else {
+ callback(undefined, obj);
+ end = true;
+ }
+ });
+
+ cb.onCharacters(function (chars) {
+ var s = stack[stack.length - 1];
+ if (s) {
+ return s['#'] += chars;
+ }
+ });
+
+ cb.onError(function (error) {
+ if ( ! end) {
+ callback(error, {});
+ }
+ });
+ });
+
+ parser.parseString(xml);
+};
17 package.json
@@ -0,0 +1,17 @@
+{
+ "name": "libxml-to-js",
+ "version": "0.1.0",
+ "main": "./lib/libxml-to-js.js",
+ "description": "XML to JavaScript object parser based on libxmljs",
+ "dependencies": {
+ "libxmljs": ">=0.x"
+ },
+ "engines": {
+ "node": ">=0.4.x"
+ },
+ "homepage": "https://github.com/SaltwaterC/libxml-to-js",
+ "author": {
+ "name": "Stefan Rusu",
+ "url": "http://www.saltwaterc.eu/"
+ }
+}
Please sign in to comment.
Something went wrong with that request. Please try again.