Name
global.JS2XML
Description
This class provides static methods for converting simple JavaScript values (including objects and arrays) to XML strings, and vice versa.
Script
// Discovery
/**
* JavaScript to XML serializer and deserializer.
*
* This class provides static methods for converting simple JavaScript values (including objects and arrays) to XML
* strings, and vice versa. It handles booleans, numbers, strings, arrays, and objects (as name:value hashmaps). It
* does NOT handle functions or typed classes.
*
* Tom Dilatush tom.dilatush@service-now.com
*/
var JS2XML = Class.create();
/*
* Converts the given value to an XML string. This method is called recursively.
*/
JS2XML.toXML = function(value, name, xml) {
var result = (xml) ? xml : '';
var type = typeof value;
if (type == 'object') {
if (value == null)
type = 'null';
else if (value instanceof String )
type = 'string';
else if (value instanceof Number )
type = 'number';
else if (value instanceof Boolean)
type = 'boolean';
else if (value instanceof Array)
type = 'array';
}
switch (type) {
case 'undefined':
result += JS2XML._toClosedTag('undefined', name);
break;
case 'null':
result += JS2XML._toClosedTag('null', name);
break;
case 'boolean':
result += JS2XML._toOpenTag('boolean', name) + (value ? 'true' : 'false') + JS2XML._toCloseTag('boolean');
break;
case 'number':
result += JS2XML._toOpenTag('number', name) + value + JS2XML._toCloseTag('number');
break;
case 'string':
value = '' + value;
result += JS2XML._toOpenTag('string', name) + JS2XML.escapeText(value) + JS2XML._toCloseTag('string');
break;
case 'array':
result += JS2XML._toOpenTag('array', name);
for (var i = 0; i < value.length; i++) {
var val = value[i];
result = JS2XML.toXML(val, '' + i, result);
}
result += JS2XML._toCloseTag('array');
break;
case 'object':
result += JS2XML._toOpenTag('object', name);
for (var propname in value) {
var val = value[propname];
result = JS2XML.toXML(val, propname, result);
}
result += JS2XML._toCloseTag('object');
break;
default:
throw new Error('Unexpected type: ' + type);
break;
}
return result;
}
/*
* Converts the given XML string to a value.
*/
JS2XML.fromXML = function(xmlStr) {
var answer;
var xml = '' + xmlStr;
_fromXML(null);
return answer;
function _fromXML(parent) {
// parse the first tag in the incoming XML...
var parser = /(<\/?)([^ >\/]+)(.*?)(\/?>)(.*)/;
var parsed = parser.exec(xml);
if (!parsed)
throw new Error('Invalid XML: ' + xml);
var opener = parsed[1];
var tagName = parsed[2];
var attrs = parsed[3];
var closer = parsed[4];
xml = parsed[5];
// parse the name, if we have one...
parser = /.*name="([^"]*)".*/;
parsed = parser.exec(attrs);
var name = parsed ? JS2XML.unescapeAttr(parsed[1]) : null;
var proper = JSUtil.has(parent) && JSUtil.notNil(name);
// this better be an open tag...
if (opener != '<')
throw new Error('Unexpected close tag ' + opener + ': ' + xml);
switch (tagName) {
case 'undefined':
verifyClosedTag();
setValue(undefined);
break;
case 'null':
verifyClosedTag();
setValue(null);
break;
case 'boolean':
verifyOpenTag();
setValue('true' == getValue('boolean'));
break;
case 'number':
verifyOpenTag();
setValue(getValue('number') - 0);
break;
case 'string':
verifyOpenTag();
setValue(getValue('string'));
break;
case 'array':
verifyOpenTag();
var arr = [];
setValue(arr);
while (!closeNext('array'))
_fromXML(arr);
break;
case 'object':
verifyOpenTag();
var obj = {};
setValue(obj);
while (!closeNext('object'))
_fromXML(obj);
break;
default:
throw new Error('Unexpected tag: ' + xml);
break;
}
return result;
function closeNext(tag) {
parser = /\s*<\/(\w*)\s*>(.*)/;
parsed = parser.exec(xml);
var result = parsed && parsed[1] == tag;
if (result)
xml = parsed[2];
return result;
}
function setValue(value) {
if (proper) {
if (parent instanceof Array)
parent[name - 0] = value;
else
parent[name] = value;
} else
answer = value;
}
function getValue(tag) {
parser = /\s*(.*?)\s*<\/(\w+)\s*>(.*)/;
parsed = parser.exec(xml);
if (!parsed || parsed[2] != tag)
throw new Error('No close tag ' + tag + ': ' + xml);
xml = parsed[3];
return JS2XML.unescapeText(parsed[1]);
}
function verifyClosedTag() {
if (closer != '/>')
throw new Error('Expected closed tag: ' + xml);
}
function verifyOpenTag() {
if (closer != '>')
throw new Error('Expected open tag: ' + xml);
}
}
}
JS2XML._toClosedTag = function(tag, name) {
return JS2XML._toStartTag(tag, name) + '/>';
}
JS2XML._toOpenTag = function(tag, name) {
return JS2XML._toStartTag(tag, name) + '>';
}
JS2XML._toCloseTag = function(tag) {
return '</' + tag + '>';
}
JS2XML._toStartTag = function(tag, name) {
return '<' + tag + (JSUtil.nil(name) ? '' : ' name="' + JS2XML.escapeAttr(name) + '"');
}
/*
* NOTE: between this banner and the following banner, several string literals are specified in an odd way: by the
* contatenation of a single character ('&') and the remainder of the HTML entity (such as 'amp;'). This method
* was employed to avoid having the entities translated into the equivalent characters when the script include is
* edited in the instance.
*/
JS2XML.AMP = /\&/g;
JS2XML.GT = /\>/g;
JS2XML.LT = /\</g;
JS2XML.QT = /\"/g;
JS2XML.AMP_ENT = new RegExp('\\&' + 'amp;', 'g');
JS2XML.GT_ENT = new RegExp('\\&' + 'gt;', 'g');
JS2XML.LT_ENT = new RegExp('\\&' + 'lt;', 'g');
JS2XML.QT_ENT = new RegExp('\\@quote\\@', 'g');
JS2XML.escapeText = function(text) {
var result = ('' + text).replace(JS2XML.AMP, '&' + 'amp;');
result = result.replace(JS2XML.LT, '&' + 'lt;');
return result.replace(JS2XML.GT, '&' + 'gt;');
}
JS2XML.unescapeText = function(text) {
var result = ('' + text).replace(JS2XML.GT_ENT, '>');
result = result.replace(JS2XML.LT_ENT, '<');
return result.replace(JS2XML.AMP_ENT, '&');
}
JS2XML.escapeAttr = function(attr) {
var result = ('' + attr).replace(JS2XML.AMP, '&' + 'amp;');
return result.replace(JS2XML.QT, '@quote@');
}
JS2XML.unescapeAttr = function(attr) {
var result = ('' + attr).replace(JS2XML.QT_ENT, '"');
return result.replace(JS2XML.AMP_ENT, '&');
}
/*
* End of odd string construction...
*/
JS2XML.prototype = {
type: "JS2XML"
};
Sys ID
cc8d91030ab3015300997340ae596167