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 convertoptions(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:
&→&<→<>→>"→"'→'
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 <test></title>
<rating>5</rating>
<comment>This works perfectly! 5 < 10 & 3 > 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, '&');
str = str.replace(/</g, '<');
str = str.replace(/>/g, '>');
str = str.replace(/"/g, '"');
str = str.replace(/'/g, ''');
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, '');
}