Comparing version 0.2.4 to 0.3.0
@@ -21,5 +21,19 @@ module.exports = function toXml(json, xml) { | ||
// First pass, extract strings only | ||
for (var i = 0; i < len; i++) { | ||
var key = keys[i]; | ||
if (typeof(obj[key]) == 'string') { | ||
if (key == '$t') { | ||
xml += obj[key]; | ||
} else { | ||
xml = xml.replace(/>$/, ''); | ||
xml += ' ' + key + "='" + obj[key] + "'>"; | ||
} | ||
} | ||
} | ||
// Second path, now handle sub-objects and arrays | ||
for (var i = 0; i < len; i++) { | ||
var key = keys[i]; | ||
if (Array.isArray(obj[key])) { | ||
@@ -37,9 +51,2 @@ var elems = obj[key]; | ||
xml += '</' + key + '>'; | ||
} else if (typeof(obj[key]) == 'string') { | ||
if (key == '$t') { | ||
xml += obj[key]; | ||
} else { | ||
xml = xml.replace(/>$/, ''); | ||
xml += ' ' + key + "='" + obj[key] + "'>"; | ||
} | ||
} | ||
@@ -46,0 +53,0 @@ } |
@@ -5,7 +5,17 @@ var expat = require('node-expat'); | ||
// This object will hold the final result. | ||
var obj = currentObject = {}; | ||
var obj = {}; | ||
var currentObject = {}; | ||
var ancestors = []; | ||
var currentElementName = null; | ||
var options = {}; //configuration options | ||
function startElement(name, attrs) { | ||
currentElementName = name; | ||
if(options.coerce) { | ||
// Looping here in stead of making coerce generic as object walk is unnecessary | ||
Object.keys(attrs).forEach(function(key) { | ||
attrs[key] = coerce(attrs[key]); | ||
}); | ||
} | ||
if (! (name in currentObject)) { | ||
@@ -39,10 +49,22 @@ currentObject[name] = attrs; | ||
function text(data) { | ||
data = data.trim(); | ||
if (!data.length) { | ||
//console.log('->' + data + '<-'); | ||
/*if (!data.trim().length) { | ||
return; | ||
}*/ | ||
if (options.trim) { | ||
data = data.trim(); | ||
} | ||
currentObject['$t'] = (currentObject['$t'] || "") + data; | ||
if (options.sanitize) { | ||
data = sanitize(data); | ||
} | ||
currentObject['$t'] = coerce((currentObject['$t'] || '') + data); | ||
} | ||
function endElement(name) { | ||
if (currentElementName !== name) { | ||
delete currentObject['$t']; | ||
} | ||
// This should check to make sure that the name we're ending | ||
@@ -64,2 +86,76 @@ // matches the name we started on. | ||
function coerce(value) { | ||
if (!options.coerce) { | ||
return value; | ||
} | ||
var num = Number(value); | ||
if (!isNaN(num)) { | ||
return num; | ||
} | ||
var _value = value.toLowerCase(); | ||
if (_value == 'true' || _value == 'yes') { | ||
return true; | ||
} | ||
if (_value == 'false' || _value == 'no') { | ||
return false; | ||
} | ||
return value; | ||
} | ||
/** | ||
* Simple sanitization. It is not intended to sanitize | ||
* malicious element values. | ||
* | ||
* character | escaped | ||
* < < | ||
* > > | ||
* ( ( | ||
* ) ) | ||
* # # | ||
* & & | ||
* " " | ||
* ' ' | ||
*/ | ||
var chars = { '<': '<', | ||
'>': '>', | ||
'(': '(', | ||
')': ')', | ||
'#': '#', | ||
'&': '&', | ||
'"': '"', | ||
"'": ''' }; | ||
function sanitize(value) { | ||
if (typeof value !== 'string') { | ||
return value; | ||
} | ||
Object.keys(chars).forEach(function(key) { | ||
value = value.replace(key, chars[key]); | ||
}); | ||
return value; | ||
} | ||
/** | ||
* Parses xml to json using node-expat. | ||
* @param {String|Buffer} xml The xml to be parsed to json. | ||
* @param {Object} _options An object with options provided by the user. | ||
* The available options are: | ||
* - object: If true, the parser returns a Javascript object instead of | ||
* a JSON string. | ||
* - reversible: If true, the parser generates a reversible JSON, mainly | ||
* characterized by the presence of the property $t. | ||
* - sanitize_values: If true, the parser escapes any element value in the xml | ||
* that has any of the following characters: <, >, (, ), #, #, &, ", '. | ||
* | ||
* @return {String|Object} A String or an Object with the JSON representation | ||
* of the XML. | ||
*/ | ||
module.exports = function(xml, _options) { | ||
@@ -74,6 +170,10 @@ var parser = new expat.Parser('UTF-8'); | ||
ancestors = []; | ||
currentElementName = null; | ||
options = { | ||
object: false, | ||
reversible: false | ||
reversible: false, | ||
coerce: true, | ||
sanitize: true, | ||
trim: true | ||
}; | ||
@@ -93,4 +193,9 @@ | ||
return JSON.stringify(obj); | ||
var json = JSON.stringify(obj); | ||
//See: http://timelessrepo.com/json-isnt-a-javascript-subset | ||
json = json.replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029'); | ||
return json; | ||
}; | ||
{ "name" : "xml2json", | ||
"version": "0.2.4", | ||
"version": "0.3.0", | ||
"author": "Andrew Turley", | ||
@@ -10,5 +10,5 @@ "email": "aturley@buglabs.net", | ||
"dependencies": { | ||
"node-expat": "1.4.1" | ||
"node-expat": ">=1.4.1 <2.0.0" | ||
} | ||
} | ||
@@ -5,3 +5,3 @@ # Simple SAX-based XML2JSON Parser. | ||
* CDATA sections | ||
* CDATA sections (*) | ||
* Processing instructions | ||
@@ -12,4 +12,6 @@ * XML declarations | ||
## Installation | ||
`npm install xml2json` | ||
## Installation | ||
``` | ||
$ npm install xml2json | ||
``` | ||
@@ -21,10 +23,72 @@ ## Usage | ||
var xml = "<foo>bar</foo>"; | ||
var json = parser.toJson(xml); //returns an string containing the json structure by default | ||
var json = parser.toJson(xml); //returns a string containing the JSON structure by default | ||
console.log(json); | ||
``` | ||
* if you want to get the Javascript object then you might want to invoke parser.toJson(xml, {object: true}); | ||
* if you want a reversible json to xml then you should use parser.toJson(xml, {reversible: true}); | ||
## API | ||
```javascript | ||
parser.toJson(xml, options); | ||
``` | ||
```javascript | ||
parser.toXml(json, options); | ||
``` | ||
### Options object | ||
Default values: | ||
```javascript | ||
var options = { | ||
object: false, | ||
reversible: false, | ||
coerce: true, | ||
sanitize: true, | ||
trim: true | ||
}; | ||
``` | ||
* **object:** Returns a Javascript object instead of a JSON string | ||
* **reversible:** Makes the JSON reversible to XML (*) | ||
* **coerce:** Makes type coercion. i.e.: numbers and booleans present in attributes and element values are converted from string to its correspondent data types. | ||
* **trim:** Removes leading and trailing whitespaces as well as line terminators in element values. | ||
* **sanitize:** Sanitizes the following characters present in element values: | ||
```javascript | ||
var chars = { | ||
'<': '<', | ||
'>': '>', | ||
'(': '(', | ||
')': ')', | ||
'#': '#', | ||
'&': '&', | ||
'"': '"', | ||
"'": ''' | ||
}; | ||
``` | ||
(*) xml2json tranforms CDATA content to JSON, but it doesn't generate a reversible structure. | ||
## License | ||
Copyright 2011 BugLabs Inc. All rights reserved. | ||
(The MIT License) | ||
Copyright 2012 BugLabs. All rights reserved. | ||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to | ||
deal in the Software without restriction, including without limitation the | ||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
sell copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
IN THE SOFTWARE. |
@@ -20,3 +20,13 @@ var fs = require('fs'); | ||
var data2 = fs.readFileSync(fixturesPath + '/' + file); | ||
result = parser.toJson(data2); | ||
if (file.indexOf('spacetext') >= 0) { | ||
result = parser.toJson(data2, {trim: false, coerce: false}); | ||
} else if (file.indexOf('coerce') >= 0) { | ||
result = parser.toJson(data2, {coerce: false}); | ||
} else if (file.indexOf('domain') >= 0) { | ||
result = parser.toJson(data2, {coerce: false}); | ||
} else if (file.indexOf('large') >= 0) { | ||
result = parser.toJson(data2, {coerce: false, trim: true, sanitize: false}); | ||
} else { | ||
result = parser.toJson(data2, {trim: false}); | ||
} | ||
@@ -29,2 +39,5 @@ var jsonFile = basename + '.json'; | ||
} | ||
/*console.log(result); | ||
console.log('============ Expected ==============='); | ||
console.log(expected)*/ | ||
assert.deepEqual(result, expected, jsonFile + ' and ' + file + ' are different'); | ||
@@ -31,0 +44,0 @@ console.log('[xml2json: ' + file + '->' + jsonFile + '] passed!'); |
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
70558
24
341
91
0
7
+ Addednode-expat@1.6.1(transitive)
- Removednode-expat@1.4.1(transitive)
Updatednode-expat@>=1.4.1 <2.0.0