//copyright Ryan Day 2010 , Joscha Feth 2013 [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(''); } 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 = 0; j < node_data[name].length; j++) { 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 = [''); return ret.join(''); }; function esc(str) { return ('' + str).replace(/&/g, '&') .replace(//g, '>') .replace(/'/g, ''') .replace(/"/g, '"') .replace(not_safe_in_xml, ''); } module.exports = function (obj, options) { if (!options) { options = { xmlHeader: { standalone: true }, prettyPrint: true, indent: " ", escape: true, }; } 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 = '' } } 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, ''); };