Converting XML to JSON failing with gs.xmlToJSON when XML contains #&13;

codycotulla
Tera Guru

Hi,

I'm trying to convert XML strings to JSON objects using gs.xmlToJSON()

I've found that if the XML contains a character entity such as 
 then the conversion fails.

For example

var xTest = "<a>Hello &#13; World</a>";

var obj = gs.xmlToJSON(xTest);

// obj is null;



var xmlDoc = new XMLDocument2();

xmlDoc.parseXML(xTest);

var tOrF = xmlDoc.isValid();

// tOrF is true 

I know that the XML is valid because the XMLDocument2.isValid() method is OK.

I also know that if I take out the & in &#13; then the conversion works.

var xTest = "<a>Hello &#13; World</a>";
xTest = "<a>Hello #13;  World</a>";

var obj = gs.xmlToJSON(xTest);

// obj is an Object.

Has anyone seen this problem? Is there a work around?

Any help is appreciated.

Thanks,

Cody

1 ACCEPTED SOLUTION

codycotulla
Tera Guru

Hi Simon,

Thanks for the suggestions! However, I'm not able to use the XMLHelper because I'm in a scoped application.

It seems that the gs.xmlToJSON() command should work because &#13; is a valid character entity. It appears all over the payload of ecc_queue records. 

I decided to go a "kludgy" route and strip out the white space characters that are common. To that end I created a script include that converts Xml to Json and that converts Json To XML. The Json to XML part, I copied from the XMLHelper Script include from ServiceNow.

Below is the content of the script include. It's called OkXMLHelper because ... it's OK for what I need!

When it comes to converting Xml to JSON. There are differences between how this works and how XMLHelper works. I've documented the differences in the description of the Script Include which I've attached to this comment. Along, with a scheduled job for testing.

var OkXMLHelper = Class.create();
OkXMLHelper.prototype = {
	
	initialize : function() {
	},
	
	toObject : function(s){
		this.toObject = function(s){
			s = this.fixXml(s);
			var obj = gs.xmlToJSON(s);
			return obj;
		};
		this.fixXml = function(s){
			//The gs.xmlToJSON() method doesn't handle character entities.
			//So I'm replacing the common ones for whitespace characters that appear
			//in the ecc_queue payload strings that I'm primarily working with.
			if(s.match(/&#/ig) != null){
				s = s.replace(/&#13;/ig, "\r");
				s = s.replace(/&#10;/ig, "\n");
				if(s.match(/&#/ig) == null){
					return s;
				}
				s = s.replace(/&#9;/ig, "\t");
				s = s.replace(/&#09;/ig, "\t");
			}
			
			return s;
		};
		return this.toObject(s);
		
	},
	
	
    toXMLStr : function(o, leaveBlanks) {
		/*
			This was copied from the XMLHelper script include provided by ServiceNow.
		*/
        var toXml = function(v, name, ind) {
		   if (typeof(v) == "function")
			  return "";
		   
            var xml = "";
            if (v instanceof Array) {
                for ( var i = 0, n = v.length; i < n; i++)
                    xml += ind + toXml(v[i], name, ind + "\t") + "\n";
            } else if (typeof (v) == "object") {
                var hasChild = false;
                xml += ind + "<" + name;
                for ( var m in v) {
                    if (m.charAt(0) == "@")
                        xml += " " + m.substr(1) + "=\"" + v[m].toString() + "\"";
                    else
                        hasChild = true;
                }
                xml += hasChild ? ">" : "/>";
                if (hasChild) {
                    for ( var m in v) {
                        if (m == "#text")
                            xml += v[m];
                        else if (m == "#cdata")
                            xml += "<![CDATA[" + v[m] + "]]>";
                        else if (m.charAt(0) != "@")
                            xml += toXml(v[m], m, ind + "\t");
                    }
                    xml += (xml.charAt(xml.length - 1) == "\n" ? ind : "") + "</" + name + ">";
                }
            } else {
                xml += ind + "<" + name + ">" + v.toString() + "</" + name + ">";
            }
            return xml;
        };

        xml = "";
        for ( var m in o)
            xml += toXml(o[m], m, "");
		
		if (leaveBlanks)
			return xml;
		
        return xml.replace(/\t|\n/g, "");
    },


	type : "OkXMLHelper"
};

 

Also if you are interested in running a scheduled job in the debugger, which is what I used for testing this, see Test Scheduled Jobs(and Fix Scripts) in the Script Debugger

Thanks,

 

Cody

 

View solution in original post

6 REPLIES 6

Community Alums
Not applicable

When you are on scoped app, your solution is, by far, the best.

Thanks for sharing it.

shallom
Tera Contributor

try

 

var XmlToJson = Class.create();
XmlToJson.prototype = {
    initialize: function() {},
    convert: function(xml) {
        var xmlDoc = new XMLDocument2();
        xmlDoc.parseXML(xml);
        var root = xmlDoc.getDocumentElement();
        if (!root) {
            gs.error("Root element is null. Unable to parse XML.");
            return null;
        }
        var jsonResult = {};
        jsonResult[root.getNodeName()] = this.parseXmlToJson(root);
        return jsonResult;
    },
    isEmptyObject: function(obj) {
        return Object.keys(obj).length === 0;
    },
    parseXmlToJson: function(node) {
        if (!node) return null; // Check if node is null

        var jsonObject = {};

        // Get attributes (if any) with a null check
        var attrs = node.getAttributes();
        if (!this.isEmptyObject(attrs)) {
            jsonObject = attrs;
        }

        // Iterate through child nodes
        var childNodes = node.getChildNodeIterator();
        if (childNodes.hasNext()) {
            while (childNodes.hasNext()) {
                var childNode = childNodes.next();
                var childName = childNode.getNodeName();
                var childValue = childNode.getNodeValue();

                // Skip text nodes
                if (childName === "#text") {
                    if (!this.isEmptyObject(attrs)) {
                        jsonObject['value'] = childValue || null;
                    } else {
                        jsonObject = childValue || null;
                    }
                    continue;
                };

                if (childNode.isCDATANode()) {
                    jsonObject[childName] = childValue;
                } else if (!jsonObject[childName]) {
                    jsonObject[childName] = this.parseXmlToJson(childNode);
                } else {
                    // Handle cases where there are multiple nodes with the same name
                    if (!Array.isArray(jsonObject[childName])) {
                        jsonObject[childName] = [jsonObject[childName]];
                    }
                    jsonObject[childName].push(this.parseXmlToJson(childNode));
                }
            }
        } else if (this.isEmptyObject(attrs)) {
            // If there are no child nodes, get the node's value
            jsonObject = node.getNodeValue() || null;
        }

        return jsonObject;
    },
    type: 'XmlToJson'
};
var xml = "XML paylod";
gs.info(JSON.stringify(new XmlToJson().convert(xml), null, 2))