Skip to main content

JSON to XML convertor

Table of Contents

Introduction
#

This script, which works with Mozilla Rhino, will convert a JSON string to an XML document.

Core function
#

jsonToXml(json, options) → string`</mark>

Parameters:

  • json (Object|Array|string|number|boolean|null) – Input data to convert
  • options (Object, optional) – Conversion options

Returns: (string) XML string representation


Supported Input Types
#

Input Type Example Output
Simple object {name: "Sarah", age: 28} <person><name>Sarah</name><age>28</age></person>
Array of primitives ["red", "green", "blue"] <palette><color>red</color><color>green</color>...</palette>
Nested objects {address: {city: "SF"}} <root><address><city>SF</city></address></root>
Arrays of objects [{id: 1}, {id: 2}] <orders><order><id>1</id></order>...</orders>
Primitive values "Hello" <message>Hello</message>
Empty arrays/null {items: [], count: null} <dataset><items></items><count></count></dataset>

Configuration Options
#

Option Type Default Description
rootName string 'root' Name for the root XML element
itemName string 'item' Name for array item elements
attrPrefix string '@' Prefix for JSON keys to treat as XML attributes
attrNodeName string 'attr' Element name for nested object properties
attrKeyName string 'attr-name' Attribute name for the key in nested property elements
prettyPrint boolean false Enable indentation and line breaks
indentChar string ' ' Indentation string (e.g., ' ')

Features
#

  • XML Escaping Automatically escapes special characters (&, <, >, ", ') in values.
  • Attribute Support JSON keys prefixed with @ (configurable) become XML attributes: {"@id": "123", name: "Test"} → Test
  • Nested Object Handling Nested objects are converted to child elements, with their properties as value elements by default.
  • Element Name Sanitization Invalid XML characters in names are replaced with _, and names starting with numbers/special chars are prefixed with _.
  • Pretty Printing Optional formatted output with configurable indentation.
  • Rhino Compatibility Designed to work with Rhino 1.7R5+, using print() for output.

XML Escaping
#

Automatically escapes special characters in values:

  • & → &amp;
  • < → &lt;
  • > → &gt;
  • " → &quot;
  • ' → &apos;

Attribute Support
#

JSON keys prefixed with @ (configurable via attrPrefix) become XML attributes:

// Input
{ "@id": "P1001", "@category": "electronics", name: "Smartphone X" }

// Output with attrPrefix: '@'
<product id="P1001" category="electronics">
  <name>Smartphone X</name>
</product>

Nested Object Handling
#

Nested objects are converted to child elements. Their properties become:

<attr attr-name="key">value</attr>

unless configured otherwise via attrNodeName and attrKeyName.

Element Name Sanitization
#

  • Invalid XML characters ([^a-zA-Z0-9_:.-]) replaced with _
  • Names starting with numbers or special chars prefixed with _

Pretty Printing
#

Optional formatted output with configurable indentation:

<root>
  <name>Value</name>
  <nested>
    <item>Content</item>
  </nested>
</root>

Rhino compatibility
#

Designed to work with Rhino 1.7R5+.

Examples Included
#

The file contains 14 usage examples demonstrating:

  • Simple and complex objects
  • Arrays (primitives & objects)
  • XML attributes via @ prefix
  • Custom indentation
  • Empty arrays and null values
  • Special character escaping
  • Custom attribute element names (attrNodeName, attrKeyName)
  • Mixed content

Example 1: Simple Object
#

JSON: {
  "name": "Sarah Johnson",
  "age": 28,
  "city": "New York"
}
XML: <person><name>Sarah Johnson</name><age>28</age><city>New York</city></person>

Example 2: Array of Primitives
#

JSON: [
  "red",
  "green",
  "blue"
]
XML: <palette>
  <color>red</color>
  <color>green</color>
  <color>blue</color>
</palette>

Example 3: Nested Objects
#

JSON: {
  "name": "TechCorp",
  "founded": 2010,
  "address": {
    "street": "123 Innovation Blvd",
    "city": "San Francisco",
    "zip": "94105"
  }
}
XML: <company>
  <name>TechCorp</name>
  <founded>2010</founded>
  <address>
    <attr attr-name="street">123 Innovation Blvd</attr>
    <attr attr-name="city">San Francisco</attr>
    <attr attr-name="zip">94105</attr>
  </address>
</company>

Example 4: Object with Attributes
#

JSON: {
  "@id": "P1001",
  "@category": "electronics",
  "name": "Smartphone X",
  "price": 599.99,
  "inStock": true
}
XML: <product id="P1001" category="electronics">
  <name>Smartphone X</name>
  <price>599.99</price>
  <inStock>true</inStock>
</product>

Example 5: Complex Structure
#

JSON: {
  "@name": "Central Library",
  "books": [
    {
      "@isbn": "978-0134853987",
      "title": "The Pragmatic Programmer",
      "author": "Andrew Hunt",
      "year": 1999
    },
    {
      "@isbn": "978-0596517748",
      "title": "JavaScript: The Good Parts",
      "author": "Douglas Crockford",
      "year": 2008
    }
  ]
}
XML: <library name="Central Library">
  <books>
    <book isbn="978-0134853987">
      <title>The Pragmatic Programmer</title>
      <author>Andrew Hunt</author>
      <year>1999</year>
    </book>
    <book isbn="978-0596517748">
      <title>JavaScript: The Good Parts</title>
      <author>Douglas Crockford</author>
      <year>2008</year>
    </book>
  </books>
</library>

Example 6: XML Escaping
#

JSON: {
  "title": "Great Product <test>",
  "rating": 5,
  "comment": "This works perfectly! 5 < 10 & 3 > 2"
}
XML: <review>
  <title>Great Product &lt;test&gt;</title>
  <rating>5</rating>
  <comment>This works perfectly! 5 &lt; 10 &amp; 3 &gt; 2</comment>
</review>

Example 7: Array of Objects
#

JSON: [
  {
    "id": "A001",
    "amount": 150.5,
    "status": "shipped"
  },
  {
    "id": "A002",
    "amount": 275.25,
    "status": "processing"
  },
  {
    "id": "A003",
    "amount": 99.99,
    "status": "delivered"
  }
]
XML: <orders>
  <order>
    <id>A001</id>
    <amount>150.5</amount>
    <status>shipped</status>
  </order>
  <order>
    <id>A002</id>
    <amount>275.25</amount>
    <status>processing</status>
  </order>
  <order>
    <id>A003</id>
    <amount>99.99</amount>
    <status>delivered</status>
  </order>
</orders>

Example 8: Empty & Null Values
#

JSON: {
  "items": [],
  "count": null,
  "active": false
}
XML: <dataset>
  <items/>
  <count/>
  <active>false</active>
</dataset>

Example 9: Custom Indentation
#

JSON: {
  "setting1": "value1",
  "setting2": "value2",
  "nested": {
    "optionA": true,
    "optionB": false
  }
}
XML: <configuration>
    <setting1>value1</setting1>
    <setting2>value2</setting2>
    <nested>
        <attr attr-name="optionA">true</attr>
        <attr attr-name="optionB">false</attr>
    </nested>
</configuration>

Example 10: Example with attr-name default
#

JSON: {
  "setting1": "value1",
  "setting2": "value2",
  "nested": {
    "optionA": true,
    "optionB": false
  }
}
XML: <configuration>
    <setting1>value1</setting1>
    <setting2>value2</setting2>
    <nested>
        <attr attr-name="optionA">true</attr>
        <attr attr-name="optionB">false</attr>
    </nested>
</configuration>

Example 11: Primitive Value
#

JSON: "Hello, World!"
XML: <message>Hello, World!</message>

Example 12: Array of Objects
#

JSON: [
  {
    "id": 1,
    "name": "Alice",
    "department": "Engineering"
  },
  {
    "id": 2,
    "name": "Bob",
    "department": "Sales"
  },
  {
    "id": 3,
    "name": "Charlie",
    "department": "HR"
  }
]
XML: <company><employeeeeeee><id>1</id><name>Alice</name><department>Engineering</department></employeeeeeee><employeeeeeee><id>2</id><name>Bob</name><department>Sales</department></employeeeeeee><employeeeeeee><id>3</id><name>Charlie</name><department>HR</department></employeeeeeee></company>

Example 13: Custom attr names
#

JSON: [
  {
    "name": "keith",
    "permissions": {
      "read": true,
      "write": false
    }
  }
]
XML: <xml><item><name>keith</name><permissions><value name="read">true</value><value name="write">false</value></permissions></item></xml>

Example 14: Empty array in object
#

JSON: [
  {
    "response": 200,
    "data": []
  }
]
XML: <xml><item><response>200</response><data/></item></xml>

Script
#

// =============================================
// Custom Examples for jsonToXml function
// Demonstrating various conversion scenarios
// =============================================

// Example 1: Simple object with custom root name
var person = {
    name: "Sarah Johnson",
    age: 28,
    city: "New York"
};
var xml = jsonToXml(person, { rootName: 'person' });
print("=== Example 1: Simple Object ===");
print("JSON:", JSON.stringify(person, null, 2));
print("XML:", xml);

// Example 2: Array of simple values with pretty printing
var colors = ["red", "green", "blue"];
var xml = jsonToXml(colors, {
    rootName: 'palette',
    itemName: 'color',
    prettyPrint: true
});
print("\n=== Example 2: Array of Primitives ===");
print("JSON:", JSON.stringify(colors, null, 2));
print("XML:", xml);

// Example 3: Nested object
var company = {
    name: "TechCorp",
    founded: 2010,
    address: {
        street: "123 Innovation Blvd",
        city: "San Francisco",
        zip: "94105"
    }
};
var xml = jsonToXml(company, {
    rootName: 'company',
    prettyPrint: true
});
print("\n=== Example 3: Nested Objects ===");
print("JSON:", JSON.stringify(company, null, 2));
print("XML:", xml);

// Example 4: Using attributes with @ prefix
var product = {
    "@id": "P1001",
    "@category": "electronics",
    name: "Smartphone X",
    price: 599.99,
    inStock: true
};
var xml = jsonToXml(product, {
    rootName: 'product',
    attrPrefix: '@',
    prettyPrint: true
});
print("\n=== Example 4: Object with Attributes ===");
print("JSON:", JSON.stringify(product, null, 2));
print("XML:", xml);

// Example 5: Complex nested structure with arrays and attributes
var library = {
    "@name": "Central Library",
    books: [
        {
            "@isbn": "978-0134853987",
            title: "The Pragmatic Programmer",
            author: "Andrew Hunt",
            year: 1999
        },
        {
            "@isbn": "978-0596517748",
            title: "JavaScript: The Good Parts",
            author: "Douglas Crockford",
            year: 2008
        }
    ]
};
var xml = jsonToXml(library, {
    rootName: 'library',
    itemName: 'book',
    attrPrefix: '@',
    prettyPrint: true
});
print("\n=== Example 5: Complex Structure ===");
print("JSON:", JSON.stringify(library, null, 2));
print("XML:", xml);

// Example 6: Mixed content with special characters (XML escaping)
var review = {
    title: "Great Product <test>",
    rating: 5,
    comment: "This works perfectly! 5 < 10 & 3 > 2"
};
var xml = jsonToXml(review, {
    rootName: 'review',
    prettyPrint: true
});
print("\n=== Example 6: XML Escaping ===");
print("JSON:", JSON.stringify(review, null, 2));
print("XML:", xml);

// Example 7: Array of objects with custom item name
var orders = [
    { id: "A001", amount: 150.50, status: "shipped" },
    { id: "A002", amount: 275.25, status: "processing" },
    { id: "A003", amount: 99.99, status: "delivered" }
];
var xml = jsonToXml(orders, {
    rootName: 'orders',
    itemName: 'order',
    prettyPrint: true
});
print("\n=== Example 7: Array of Objects ===");
print("JSON:", JSON.stringify(orders, null, 2));
print("XML:", xml);

// Example 8: Empty array and null values
var data = {
    items: [],
    count: null,
    active: false
};
var xml = jsonToXml(data, {
    rootName: 'dataset',
    prettyPrint: true
});
print("\n=== Example 8: Empty & Null Values ===");
print("JSON:", JSON.stringify(data, null, 2));
print("XML:", xml);

// Example 9: Using different indentation
var config = {
    setting1: "value1",
    setting2: "value2",
    nested: {
        optionA: true,
        optionB: false
    }
};
var xml = jsonToXml(config, {
    rootName: 'configuration',
    prettyPrint: true,
    indentChar: '    '  // 4 spaces for indentation
});
print("\n=== Example 9: Custom Indentation ===");
print("JSON:", JSON.stringify(config, null, 2));
print("XML:", xml);

// Example 10: Using different indentation
var config = {
    setting1: "value1",
    setting2: "value2",
    nested: {
        optionA: true,
        optionB: false
    }
};
var xml = jsonToXml(config, {
    rootName: 'configuration',
    prettyPrint: true,
    indentChar: '    '  // 4 spaces for indentation
});
print("\n=== Example 10: Example with attr-name default ===");
print("JSON:", JSON.stringify(config, null, 2));
print("XML:", xml);

// Example 11: Primitive value at root
var message = "Hello, World!";
var xml = jsonToXml(message, {
    rootName: 'message'
});
print("\n=== Example 11: Primitive Value ===");
print("JSON:", JSON.stringify(message));
print("XML:", xml);


// Example 12: Array of objects
var employees = [
    {id: 1, name: "Alice", department: "Engineering"},
    {id: 2, name: "Bob", department: "Sales"},
    {id: 3, name: "Charlie", department: "HR"}
];
var xml = jsonToXml(employees, {
    rootName: 'company',
    itemName: 'employeeeeeee'
});
print("\n=== Example 12: Array of Objects ===");
print("JSON:", JSON.stringify(employees, null, 2));
print("XML:", xml);

// Example 13: Custom attr names
var employees = [{"name":"keith","permissions":{"read":true,"write":false}}];
var xml = jsonToXml(employees, {
    rootName: 'xml',
    itemName: 'item',
    attrNodeName: 'value',
    attrKeyName: 'name'
});
print("\n=== Example 13: Custom attr names ===");
print("JSON:", JSON.stringify(employees, null, 2));
print("XML:", xml);

// Example 14: Empty array in object
var employees = [{"response":200,"data":[]}];
var xml = jsonToXml(employees, {
    rootName: 'xml',
    itemName: 'item',
    attrNodeName: 'value',
    attrKeyName: 'name'
});
print("\n=== Example 14: Empty array in object ===");
print("JSON:", JSON.stringify(employees, null, 2));
print("XML:", xml);

/**
 * json_to_xml.js - Rhino-compatible JSON to XML converter
 * Works with Rhino 1.7R5 and later
 */

/**
 * Converts a JSON object to XML string
 * NOTE: JSON object properties with simple values become child XML elements.
 * Nested objects are converted to elements with <attrNodeName attrKeyName="key">value</attrNodeName> children.
 * @param {Object|Array|string|number|boolean} json - The JSON data to convert
 * @param {Object} [options] - Conversion options
 * @param {string} [options.rootName='root'] - Name for the root XML element
 * @param {string} [options.itemName='item'] - Name for array item elements
 * @param {string} [options.attrPrefix='@'] - Prefix for attributes in JSON (e.g., {"@id": 1})
 * @param {string} [options.attrNodeName='attr'] - Element name for nested object properties
 * @param {string} [options.attrKeyName='attr-name'] - Attribute name for the key in nested object property elements
 * @param {boolean} [options.prettyPrint=false] - Format XML with indentation
 * @param {string} [options.indentChar='  '] - Indentation string
 * @returns {string} XML string
 */
function jsonToXml(json, options) {
    options = options || {};
    var rootName = options.rootName || 'root';
    var itemName = options.itemName || 'item';
    var attrPrefix = options.attrPrefix || '@';
    var attrNodeName = options.attrNodeName || 'attr';
    var attrKeyName = options.attrKeyName || 'attr-name';
    var prettyPrint = options.prettyPrint || false;
    var indentChar = options.indentChar || '  ';

    function escapeXml(str) {
        if (str == null) return '';
        str = String(str);
        str = str.replace(/&/g, '&amp;');
        str = str.replace(/</g, '&lt;');
        str = str.replace(/>/g, '&gt;');
        str = str.replace(/"/g, '&quot;');
        str = str.replace(/'/g, '&apos;');
        return str;
    }

    function sanitizeElementName(name) {
        if (name == null || name === '') {
            return 'null';
        }
        name = String(name);
        // Replace invalid XML element name characters
        name = name.replace(/[^a-zA-Z0-9_:.-]/g, '_');
        // Ensure it doesn't start with a number or special char
        if (!/^[a-zA-Z_]/.test(name)) {
            name = '_' + name;
        }
        return name;
    }

    function isArray(arg) {
        return Object.prototype.toString.call(arg) === '[object Array]';
    }

    function getKeys(obj) {
        var keys = [];
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                keys.push(key);
            }
        }
        return keys;
    }

    function hasKeys(obj) {
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                return true;
            }
        }
        return false;
    }

    function buildXml(obj, name, indent) {
        var xml = '';
        var attributes = {};
        var children = {};

        // Sanitize element name
        name = sanitizeElementName(name);

        // Handle null/undefined
        if (obj == null) {
            return indent + '<' + name + '/>';
        }

        // Separate attributes from children
        if (typeof obj === 'object' && !isArray(obj)) {
            var keys = getKeys(obj);
            for (var i = 0; i < keys.length; i++) {
                var key = keys[i];
                if (key.indexOf(attrPrefix) === 0) {
                    var attrName = key.substring(attrPrefix.length);
                    attrName = sanitizeElementName(attrName);
                    attributes[attrName] = obj[key];
                } else {
                    var sanitizedKey = sanitizeElementName(key);
                    children[sanitizedKey] = obj[key];
                }
            }
        }

        // Build attribute string
        var attrString = '';
        var attrKeys = getKeys(attributes);
        for (var j = 0; j < attrKeys.length; j++) {
            var attr = attrKeys[j];
            attr = sanitizeElementName(attr);
            attrString += ' ' + attr + '="' + escapeXml(attributes[attr]) + '"';
        }

        // Start tag
        xml += indent + '<' + name + attrString;

        // Handle different types
        if (typeof obj === 'object') {
            if (isArray(obj)) {
                if (obj.length === 0) {
                    xml += '/>';
                } else {
                    xml += '>';
                    if (prettyPrint) xml += '\n';
                    for (var k = 0; k < obj.length; k++) {
                        xml += buildXml(obj[k], itemName, indent + (prettyPrint ? indentChar : ''));
                        if (prettyPrint) xml += '\n';
                    }
                    xml += indent + '</' + name + '>';
                }
            } else if (hasKeys(children)) {
                xml += '>';
                if (prettyPrint) xml += '\n';
                var childKeys = getKeys(children);
                for (var m = 0; m < childKeys.length; m++) {
                    var childName = childKeys[m];
                    var childValue = children[childName];
                    // Sanitize child element name
                    childName = sanitizeElementName(childName);
                    // If child value is an object (not array), convert its properties to <attr> elements
                    if (typeof childValue === 'object' && childValue !== null && !isArray(childValue)) {
                        xml += indent + (prettyPrint ? indentChar : '') + '<' + childName + '>';
                        if (prettyPrint) xml += '\n';
                        var nestedKeys = getKeys(childValue);
                        for (var n = 0; n < nestedKeys.length; n++) {
                            var nestedKey = nestedKeys[n];
                            var nestedValue = childValue[nestedKey];
                            // Skip @ prefixed keys as they are already handled as attributes
                            if (nestedKey.indexOf(attrPrefix) === 0) continue;
                            nestedKey = sanitizeElementName(nestedKey);
                            xml += indent + (prettyPrint ? indentChar + indentChar : '');
                            xml += '<' + attrNodeName + ' ' + attrKeyName + '="' + escapeXml(nestedKey) + '">';
                            xml += escapeXml(nestedValue);
                            xml += '</' + attrNodeName + '>';
                            if (prettyPrint) xml += '\n';
                        }
                        xml += indent + (prettyPrint ? indentChar : '') + '</' + childName + '>';
                        if (prettyPrint) xml += '\n';
                    } else {
                        xml += buildXml(childValue, childName, indent + (prettyPrint ? indentChar : ''));
                        if (prettyPrint) xml += '\n';
                    }
                }
                xml += indent + '</' + name + '>';
            } else {
                xml += '/>';
            }
        } else {
            xml += '>' + escapeXml(obj) + '</' + name + '>';
        }

        return xml;
    }

    // Handle array at root level
    if (isArray(json)) {
        var arrXml = '';
        if (prettyPrint) arrXml += '\n';
        for (var i = 0; i < json.length; i++) {
            arrXml += buildXml(json[i], itemName, (prettyPrint ? indentChar : ''));
            if (prettyPrint) arrXml += '\n';
        }
        return '<' + rootName + '>' + arrXml + '</' + rootName + '>';
    }

    // Handle primitive at root level
    if (typeof json !== 'object') {
        return '<' + rootName + '>' + escapeXml(json) + '</' + rootName + '>';
    }

    // Handle object at root level
    return buildXml(json, rootName, '');
}