/** * JSON to XML jQuery plugin. Provides quick way to convert JSON object to XML * string. To some extent, allows control over XML output. * Just as jQuery itself, this plugin is released under both MIT & GPL licences. * * @version 1.02 * @author Micha Korecki, www.michalkorecki.com */ (function($) { /** * Converts JSON object to XML string. * * @param json object to convert * @param options additional parameters * @return XML string */ $.json2xml = function(json, options) { settings = {}; settings = $.extend(true, settings, defaultSettings, options || { }); return ''+convertToXml(json, settings.rootTagName, '', 0); }; var defaultSettings = { formatOutput: true, formatTextNodes: false, indentString: ' ', rootTagName: 'root', ignore: [], replace: [], nodes: [], ///TODO: exceptions system exceptions: [] }; /** * This is actual settings object used throught plugin, default settings * are stored separately to prevent overriding when using multiple times. */ var settings = {}; /** * Core function parsing JSON to XML. It iterates over object properties and * creates XML attributes appended to main tag, if property is primitive * value (eg. string, number). * Otherwise, if it's array or object, new node is created and appened to * parent tag. * You can alter this behaviour by providing values in settings.ignore, * settings.replace and settings.nodes arrays. * * @param json object to parse * @param tagName name of tag created for parsed object * @param parentPath path to properly identify elements in ignore, replace * and nodes arrays * @param depth current element's depth * @return XML string */ var convertToXml = function(json, tagName, parentPath, depth) { var suffix = (settings.formatOutput) ? '\r\n' : ''; var indent = (settings.formatOutput) ? getIndent(depth) : ''; var xmlTag = indent + '<' + tagName; var children = ''; for (var key in json) { if (json.hasOwnProperty(key)) { var propertyPath = parentPath + key; var propertyName = getPropertyName(parentPath, key); // element not in ignore array, process if ($.inArray(propertyPath, settings.ignore) == -1) { // array, create new child element if ($.isArray(json[key])) { children += createNodeFromArray(json[key], propertyName, propertyPath + '.', depth + 1, suffix); } // object, new child element aswell else if (typeof(json[key]) === 'object') { children += convertToXml(json[key], propertyName, propertyPath + '.', depth + 1); } // primitive value property as attribute else { // unless it's explicitly defined it should be node if ( propertyName.indexOf('@')==-1) { children += createTextNode(propertyName, json[key], depth, suffix); } else { propertyName = propertyName.replace('@',''); xmlTag += ' ' + propertyName + '="' + json[key] + '"'; } } } } } // close tag properly if (children !== '') { xmlTag += '>' + suffix + children + indent + '' + suffix; } else { xmlTag += '/>' + suffix; } return xmlTag; }; /** * Creates indent string for provided depth value. See settings for details. * * @param depth * @return indent string */ var getIndent = function(depth) { var output = ''; for (var i = 0; i < depth; i++) { output += settings.indentString; } return output; }; /** * Checks settings.replace array for provided name, if it exists returns * replacement name. Else, original name is returned. * * @param parentPath path to this element's parent * @param name name of element to look up * @return element's final name */ var getPropertyName = function(parentPath, name) { var index = settings.replace.length; var searchName = parentPath + name; while (index--) { // settings.replace array consists of {original : replacement} // objects if (settings.replace[index].hasOwnProperty(searchName)) { return settings.replace[index][searchName]; } } return name; }; /** * Creates XML node from javascript array object. * * @param source * @param name XML element name * @param path parent element path string * @param depth * @param suffix node suffix (whether to format output or not) * @return XML tag string for provided array */ var createNodeFromArray = function(source, name, path, depth, suffix) { var xmlNode = ''; if (source.length > 0) { for (var index in source) { // array's element isn't object - it's primitive value, which // means array might need to be converted to text nodes if (typeof(source[index]) !== 'object') { // empty strings will be converted to empty nodes if (source[index] === "") { xmlNode += getIndent(depth) + '<' + name + '/>' + suffix; } else { var textPrefix = (settings.formatTextNodes) ? suffix + getIndent(depth + 1) : ''; var textSuffix = (settings.formatTextNodes) ? suffix + getIndent(depth) : ''; xmlNode += getIndent(depth) + '<' + name + '>' + textPrefix + source[index] + textSuffix + '' + suffix; } } // else regular conversion applies else { xmlNode += convertToXml(source[index], name, path, depth); } } } // array is empty, also creating empty XML node else { xmlNode += getIndent(depth) + '<' + name + '/>' + suffix; } return xmlNode; }; /** * Creates node containing text only. * * @param name node's name * @param text node text string * @param parentDepth this node's parent element depth * @param suffix node suffix (whether to format output or not) * @return XML tag string */ var createTextNode = function(name, text, parentDepth, suffix) { // unformatted text node: value // formatting includes value indentation and new lines var textPrefix = (settings.formatTextNodes) ? suffix + getIndent(parentDepth + 2) : ''; var textSuffix = (settings.formatTextNodes) ? suffix + getIndent(parentDepth + 1) : ''; var xmlNode = getIndent(parentDepth + 1) + '<' + name + '>' + textPrefix + text + textSuffix + '' + suffix; return xmlNode; }; })(jQuery);