169 lines
5.3 KiB
JavaScript
169 lines
5.3 KiB
JavaScript
//copyright Ryan Day 2010 <http://ryanday.org>, Joscha Feth 2013 <http://www.feth.com> [MIT Licensed]
|
|
|
|
var element_start_char =
|
|
"a-zA-Z_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FFF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD";
|
|
var element_non_start_char = "\-.0-9\u00B7\u0300-\u036F\u203F\u2040";
|
|
var element_replace = new RegExp("^([^" + element_start_char + "])|^((x|X)(m|M)(l|L))|([^" + element_start_char + element_non_start_char + "])", "g");
|
|
var not_safe_in_xml = /[^\x09\x0A\x0D\x20-\xFF\x85\xA0-\uD7FF\uE000-\uFDCF\uFDE0-\uFFFD]/gm;
|
|
|
|
var objKeys = function (obj) {
|
|
var l = [];
|
|
if (obj instanceof Object) {
|
|
for (var k in obj) {
|
|
if (obj.hasOwnProperty(k)) {
|
|
l.push(k);
|
|
}
|
|
}
|
|
}
|
|
return l;
|
|
};
|
|
var process_to_xml = function (node_data, options) {
|
|
|
|
var makeNode = function (name, content, attributes, level, hasSubNodes) {
|
|
var indent_value = options.indent !== undefined ? options.indent : "\t";
|
|
var indent = options.prettyPrint ? '\n' + new Array(level).join(indent_value) : '';
|
|
if (options.removeIllegalNameCharacters) {
|
|
name = name.replace(element_replace, '_');
|
|
}
|
|
|
|
var node = [indent, '<', name, (attributes || '')];
|
|
if (content && content.length > 0) {
|
|
node.push('>')
|
|
node.push(content);
|
|
hasSubNodes && node.push(indent);
|
|
node.push('</');
|
|
node.push(name);
|
|
node.push('>');
|
|
} else {
|
|
node.push('/>');
|
|
}
|
|
return node.join('');
|
|
};
|
|
|
|
return (function fn(node_data, node_descriptor, level) {
|
|
var type = typeof node_data;
|
|
if ((Array.isArray) ? Array.isArray(node_data) : node_data instanceof Array) {
|
|
type = 'array';
|
|
} else if (node_data instanceof Date) {
|
|
type = 'date';
|
|
}
|
|
|
|
switch (type) {
|
|
//if value is an array create child nodes from values
|
|
case 'array':
|
|
var ret = [];
|
|
node_data.map(function (v) {
|
|
ret.push(fn(v, 1, level + 1));
|
|
//entries that are values of an array are the only ones that can be special node descriptors
|
|
});
|
|
options.prettyPrint && ret.push('\n');
|
|
return ret.join('');
|
|
break;
|
|
|
|
case 'date':
|
|
// cast dates to ISO 8601 date (soap likes it)
|
|
return node_data.toJSON ? node_data.toJSON() : node_data + '';
|
|
break;
|
|
|
|
case 'object':
|
|
var nodes = [];
|
|
for (var name in node_data) {
|
|
if (node_data.hasOwnProperty(name)) {
|
|
if (node_data[name] instanceof Array) {
|
|
for (var j in node_data[name]) {
|
|
if (node_data[name].hasOwnProperty(j))
|
|
nodes.push(makeNode(name, fn(node_data[name][j], 0, level + 1), null, level + 1, objKeys(node_data[name][j]).length));
|
|
}
|
|
} else {
|
|
nodes.push(makeNode(name, fn(node_data[name], 0, level + 1), null, level + 1));
|
|
}
|
|
}
|
|
}
|
|
options.prettyPrint && nodes.length > 0 && nodes.push('\n');
|
|
return nodes.join('');
|
|
break;
|
|
|
|
case 'function':
|
|
return node_data();
|
|
break;
|
|
|
|
default:
|
|
return options.escape ? esc(node_data) : '' + node_data;
|
|
}
|
|
|
|
}(node_data, 0, 0))
|
|
};
|
|
|
|
|
|
var xml_header = function (standalone) {
|
|
var ret = ['<?xml version="1.0" encoding="UTF-8"'];
|
|
|
|
if (standalone) {
|
|
ret.push(' standalone="yes"');
|
|
}
|
|
ret.push('?>');
|
|
|
|
return ret.join('');
|
|
};
|
|
|
|
function esc(str) {
|
|
return ('' + str).replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/'/g, ''')
|
|
.replace(/"/g, '"')
|
|
.replace(not_safe_in_xml, '');
|
|
}
|
|
|
|
var json2xml = function (obj, options) {
|
|
|
|
if (!options) {
|
|
options = {
|
|
xmlHeader: {
|
|
standalone: true
|
|
},
|
|
prettyPrint: true,
|
|
indent: " "
|
|
};
|
|
}
|
|
|
|
if (typeof obj == 'string') {
|
|
try {
|
|
obj = JSON.parse(obj.toString());
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
var xmlheader = '';
|
|
var docType = '';
|
|
if (options) {
|
|
if (typeof options == 'object') {
|
|
// our config is an object
|
|
|
|
if (options.xmlHeader) {
|
|
// the user wants an xml header
|
|
xmlheader = xml_header(!!options.xmlHeader.standalone);
|
|
}
|
|
|
|
if (typeof options.docType != 'undefined') {
|
|
docType = '<!DOCTYPE ' + options.docType + '>'
|
|
}
|
|
} else {
|
|
// our config is a boolean value, so just add xml header
|
|
xmlheader = xml_header();
|
|
}
|
|
}
|
|
options = options || {}
|
|
|
|
var ret = [
|
|
xmlheader,
|
|
(options.prettyPrint && docType ? '\n' : ''),
|
|
docType,
|
|
process_to_xml(obj, options)
|
|
];
|
|
return ret.join('').replace(/\n{2,}/g, '\n').replace(/\s+$/g, '');
|
|
};
|
|
|
|
module.exports = json2xml;
|