Linearize or unindent XML in javascript - javascript

I wonder how to linearize or unindent an XML text using Javascript.
This post Unindent or linearize XML describes how to do it using Java, but I saw no example using JavaScript.

As I indicated in the comment on your answer, regex is not a reliable way to do this. A much more reliable approach is to parse the XML, remove any text nodes that are all whitespace, and then re-serialize it:
function parseXml(txt) {
var parser, xmlDoc;
if (window.DOMParser) {
parser = new DOMParser();
xmlDoc = parser.parseFromString(txt, "text/xml");
} else // Internet Explorer
{
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = false;
xmlDoc.loadXML(txt);
}
return xmlDoc;
}
function serializeXml(node) {
try {
// XMLSerializer exists in certain browsers
var serializer = new XMLSerializer();
return serializer.serializeToString(node);
} catch (e) {
// Internet Explorer has a different approach to serializing XML
return elem.xml;
}
}
function removeWhitespace(node) {
if (node.childNodes && node.childNodes.length) {
Array.prototype.slice.call(node.childNodes).forEach(removeWhitespace);
}
if ((node.nodeType === 3 || node.nodeType === 4) &&
/^[ \r\n\t]*$/.test(node.textContent)) {
node.parentNode.removeChild(node);
}
}
var startXml = '<products>\n\t<product>\n\t\t<code>1234</code>\n\t\t<name>Widget 3000</name>\n\t</product>\n</products>'
console.log('Before:');
console.log(startXml);
var dom = parseXml(startXml);
removeWhitespace(dom);
var endXml = serializeXml(dom);
console.log('After:');
console.log(endXml);

As tebs1200 suggested, I ported (and even improved) the regular expression from the Java post to Javascript.
Here it is:
// This Javascript function is to linearize and return the XML input String
function linearize(xml) {
return (xml!= null) ? xml.trim().replace(/(>|>){1,1}( |\t|\n|\r|\s)*(<|<){1,1}/g, "$1$3") : null;
}

Related

Not able to load xml file in IE8

I'm trying to find the solution to load xml file and retrieve element value by tag name but stuck at the final step. Code is working in all other modern browsers except IE8.
function getXML(xmlStr) {
if (window.DOMParser) {
alert("window.DOMParser");
return (new window.DOMParser()).parseFromString(xmlStr, "text/xml");
} else if (typeof window.ActiveXObject != "undefined" && new window.ActiveXObject("Microsoft.XMLDOM")) {
alert("Microsoft.XMLDOM");
var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = "false";
xmlDoc.loadXML(xmlStr);
alert("xmlDoc");
return xmlDoc;
} else {
return null;
}
}
$(document).ready(function() {
var xmlStr = "<xml><elem>element1</elem><elem>element2</elem></xml>";
var xmlDoc = getXML(xmlStr);
$("#result").html("<b>Elemtent:</b> " + JSON.stringify(xmlDoc) + "<br/><br>");
});
I've created jsfiddle here which is the modification of the actual post answer by Tim Down.
I've tried many ways but no luck at all. Is it possible to load xml and get values in IE8?

XSLT not working on IE 11, doesn't transform xml

Trying to render XSLT stylesheet that's coming from API, thought it's working fine on Chrome, FF except IE.
I tried using the example from w3c which works but that's calling the XML and XSLT from a file, where as mine is coming from AJAX call success response.
W3school sample XSLT sample
My version is this
function getJson() {
$.get(url)..
var json2XMLResult = J2XML.json2xml_str(data);
getResultXsl(json2XMLResult )
}
function getResultXsl(json2xml) {
$.get(url)
.then(function (data) {
let resDefinition = data.Results.ResponseDisplayDefinition;
let xmlString = '<?xml version="1.0"?><Response>' + json2xml + '</Response>';
if (typeof DOMParser != "undefined") {
parseXml = function (xmlStr) {
return (new DOMParser()).parseFromString(xmlStr, "text/xml");
};
}
else if (typeof ActiveXObject != "undefined" &&
new ActiveXObject("Microsoft.XMLDOM")) {
parseXml = function (xmlStr) {
var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = "false";
xmlDoc.loadXML(xmlStr);
return xmlDoc;
};
}
else {
throw new Error("No XML parser found");
}
displayResult(xmlString, resDefinition);
})
}
Displaying XSLT in the html, the alert() below does show whether you're trying to render it on Chrome or IE,
function displayResult(xmlStrToConvert, xslStrToConvert) {
var xmlConverted = parseXml(xmlStrToConvert);
var xslConverted = parseXml(xslStrToConvert);
if (window.ActiveXObject || "ActiveXObject" in window) {
alert('It is IE but not showing anything');
var ex = xmlConverted.transformNode(xslConverted)
$('#xmlJson').append(ex);
} else {
alert('its not IE');
// code for Chrome, Firefox, Opera, etc.
var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(xslConverted);
var resultDocument = xsltProcessor.transformToFragment(xmlConverted, document);
$('#xmlJson').append(resultDocument);
}
}
Also tried var ex= xmlConverted.transformToFragment(xslConverted, document);
Can someone point-out what's wrong with this? Also couldn't open dev tool on IE11 which is harder to debug, but I can tell its something wrong with my code above.
Edit
Ajax Call with beforeSend can someone check if the below code is fine, though the transformNode() is returning Object doesn't support property or method 'transformNode' or XSLTProcessor() not defined
function transformXML(json2xml) {
$.ajax({
type: 'GET',
url: window.parent.__env.apiManagement + 'Preview/TypeDefinition?objectName=' + apiObjectResponse,
beforeSend: function (xhr, settings) {
if (window.ActiveXObject) {
xhr = new ActiveXObject("Msxml2.XMLHTTP");
}
else {
xhr = new XMLHttpRequest();
}
try { xhr.responseType = "msxml-document"; } catch (err) { }
},
success: function (data, status, xhr) {
var parseXml = new DOMParser();
var xslStylesheet = parseXml.parseFromString(data.Results.ResponseDisplayDefinition, "text/xml");
var xmlString = '<?xml version="1.0"?><Response>' + json2xml + '</Response>';
var convertedXML = parseXml.parseFromString(xmlString, "text/xml");
// // cross-browser logic omitted for simplicity
if(window.ActiveXObject || xhr.responseType == "msxml-document") {
var ex = convertedXML.transformNode(xslStylesheet);
console.log('>>> ', convertedXML)
alert(xmlString)
$('#xmlJson').append(ex);
}
// code for Chrome, Firefox, Opera, etc.
else if (document.implementation && document.implementation.createDocument) {
var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(xslStylesheet);
var resultDocument = xsltProcessor.transformToFragment(convertedXML, document);
$('#xmlJson').append(resultDocument);
}
}
});
}
IE 11 supports DOMParser but using it builds an IE XML DOM document which does not have any support for XSLT. So you at least need to change the order of checks, if you are coding for IE and want to do XSLT then make sure you create an MSXML DOM document using ActiveXObject, then you can use transformNode on it.
As you seem to want to parse XML and XSLT from strings and then use client-side XSLT transformation I would suggest to use an approach like in https://martin-honnen.github.io/xslt/2016/test2016123001.html, which does
function parseXmlStringForTransformation(xml) {
try {
var doc = new ActiveXObject('Msxml2.DOMDocument.6.0');
doc.loadXML(xml);
return doc;
}
catch (e) {
var domParser = new DOMParser();
var doc = domParser.parseFromString(xml, 'application/xml');
return doc;
}
}
and then uses XSLTProcessor where supported or the corresponding MSXML 6 ActiveX XSLT API to run the transformation:
function transform(xmlDoc, xslDoc, xsltParams, targetElement) {
if (typeof XSLTProcessor !== 'undefined') {
var proc = new XSLTProcessor();
proc.importStylesheet(xslDoc);
for (var prop in xsltParams) {
proc.setParameter(null, prop, xsltParams[prop]);
}
var resultFrag = proc.transformToFragment(xmlDoc, targetElement.ownerDocument);
targetElement.textContent = '';
targetElement.appendChild(resultFrag);
}
else {
var template = new ActiveXObject('Msxml2.XslTemplate.6.0');
template.stylesheet = xslDoc;
var proc = template.createProcessor();
for (var prop in xsltParams) {
proc.addParameter(prop, xsltParams[prop]);
}
proc.input = xmlDoc;
proc.transform();
var resultHTML = proc.output;
targetElement.innerHTML = resultHTML;
}
}
You can then use that as in
document.addEventListener('DOMContentLoaded', function() {
transform(
parseXmlStringForTransformation('<root>...<\/root>'),
parseXmlStringForTransformation('<xsl:stylesheet ...>...<\/xsl:stylesheet>'),
{ }, // empty parameter object if you don't want to pass parameters from Javascript to XSLT
document.getElementById('d1') // target element in your HTML to insert the transformation result into
);
})

Object doesn't support property or method 'transformNode' in Internet Explorer 10 (Windows 8)

I am having some JavaScript issues that seem to only occur in Internet Explorer 10 on Windows 8 (IE 7, 8, and 9 all work fine). The basic jist of what I am doing is getting XML and XSL from a web service and then transforming them in JavaScript to render on the page using the Sys.Net.XMLDOM object.
XMLDOM = Sys.Net.XMLDOM;
var xsl = // XSL gotten from somewhere else
var xmlString = // XML gotten from somewhere else as a string...
var xml = new XMLDOM(xmlString);
var content = xml.transformNode(xsl);
When I use the above code in IE 10, I get:
Object doesn't support property or method 'transformNode'
Any ideas on why Internet Explorer 10 is doing this?
EDIT
I have also tried this:
xmldoc = new ActiveXObject("Msxml2.DOMDocument");
xmldoc.async = false;
xmldoc.load(xml);
xsldoc = new ActiveXObject("Msxml2.DOMDocument");
xsldoc.async = false;
xsldoc.load(xsl);
var content = xmldoc.transformNode(xsldoc);
Which works in all previous versions of IE, but in IE 10 I get:
Reference to undeclared namespace prefix: 'atom'.
IE 9 and grater doesn't support it, try this function (found online)
function TransformToHtmlText(xmlDoc, xsltDoc) {
if (typeof (XSLTProcessor) != "undefined") { // FF, Safari, Chrome etc
var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(xsltDoc);
var xmlFragment = xsltProcessor.transformToFragment(xmlDoc, document);
return GetXmlStringFromXmlDoc(xmlFragment);
}
if (typeof (xmlDoc.transformNode) != "undefined") { // IE6, IE7, IE8
return xmlDoc.transformNode(xsltDoc);
}
else {
try { // IE9 and grater
if (window.ActiveXObject) {
var xslt = new ActiveXObject("Msxml2.XSLTemplate");
var xslDoc = new ActiveXObject("Msxml2.FreeThreadedDOMDocument");
xslDoc.loadXML(xsltDoc.xml);
xslt.stylesheet = xslDoc;
var xslProc = xslt.createProcessor();
xslProc.input = xmlDoc;
xslProc.transform();
return xslProc.output;
}
}
catch (e) {
alert("The type [XSLTProcessor] and the function [XmlDocument.transformNode] are not supported by this browser, can't transform XML document to HTML string!");
return null;
}
}
}
var content = TransformToHtmlText(xml, xsl);
Found the answer: http://blogs.msdn.com/b/ie/archive/2012/07/19/xmlhttprequest-responsexml-in-ie10-release-preview.aspx
IE 10 requires using an XMLHttpRequest with the responseType set as "msxml-document". Once I switched the code over to that, everything works perfectly in all browsers:
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else {
xhr = new ActiveXObject("Microsoft.XMLHTTP"); // For IE 6
}
xhr.open("GET", url, false);
try { xhr.responseType = "msxml-document"; } catch (e) { };
xhr.send();
I had the same problem with IE 9 and none of the answers helped until I stopped trying to load the xslt file using jQuery. I loaded the file with a script as documented in: https://msdn.microsoft.com/en-us/library/ms762796%28v=vs.85%29.aspx.
I was then able to use the transformNode() function. Here is the script that they gave:
<HTML>
<HEAD>
<TITLE>sample</TITLE>
<SCRIPT language = "javascript">
function init()
{
var srcTree =
new ActiveXObject("Msxml2.DOMDocument.6.0");
srcTree.async=false;
// You can substitute other XML file names here.
srcTree.load("hello.xml");
var xsltTree =
new ActiveXObject("Msxml2.DOMDocument.6.0");
xsltTree.async = false;
// You can substitute other XSLT file names here.
xsltTree.load("hello.xsl");
resTree.innerHTML = srcTree.transformNode(xsltTree);
}
</SCRIPT>
</HEAD>
<BODY onload = "init()" >
<div id="resTree"></div>
</BODY>
</HTML>
Firstly credit to Roel van Lisdonk who published the function Sheik Heera shared.
I found this function as it was didn't work in Chrome, because of GetXmlStringFromXmlDoc() so I used the XMLSerializer:
So for example:
if (typeof(GetXmlStringFromXmlDoc)!= "undefined")
{
return GetXmlStringFromXmlDoc(xmlFragment);
}
else
{
// chrome friendly
// get a xml serializer object
var xmls = new XMLSerializer();
// convert dom into string
var sResult = xmls.serializeToString(xmlFragment);
//extract contents of transform iix node if it is present
if (sResult.indexOf("<transformiix:result") > -1)
{
sResult = sResult.substring(sResult.indexOf(">") + 1, sResult.lastIndexOf("<"));
}
return sResult;
}
The revised function is now:
function TransformToHtmlText(xmlDoc, xsltDoc)
{
// 1.
if (typeof (XSLTProcessor) != "undefined")
{
var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(xsltDoc);
var xmlFragment = xsltProcessor.transformToFragment(xmlDoc, document);
if (typeof(GetXmlStringFromXmlDoc)!= "undefined")
{
return GetXmlStringFromXmlDoc(xmlFragment);
}
else
{
// chrome friendly
// get a xml serializer object
var xmls = new XMLSerializer();
// convert dom into string
var sResult = xmls.serializeToString(xmlFragment);
//extract contents of transform iix node if it is present
if (sResult.indexOf("<transformiix:result") > -1)
{
sResult = sResult.substring(sResult.indexOf(">") + 1, sResult.lastIndexOf("<"));
}
return sResult;
}
}
// 2.
if (typeof (xmlDoc.transformNode) != "undefined")
{
return xmlDoc.transformNode(xsltDoc);
}
else {
var activeXOb = null;
try { activeXOb = new ActiveXObject("Msxml2.XSLTemplate"); } catch (ex) {}
try {
// 3
if (activeXOb)
{
var xslt = activeXOb;
var xslDoc = new ActiveXObject("Msxml2.FreeThreadedDOMDocument");
xslDoc.loadXML(xsltDoc.xml);
xslt.stylesheet = xslDoc;
var xslProc = xslt.createProcessor();
xslProc.input = xmlDoc;
xslProc.transform();
return xslProc.output;
}
}
catch (e)
{
// 4
alert("The type [XSLTProcessor] and the function [XmlDocument.transformNode] are not supported by this browser, can't transform XML document to HTML string!");
return null;
}
}
}

Safari Won't Work With Microsoft.XMLDOM ActiveX Object

I'm designing a client side script that will read an XML file and display it, like this:
var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
function loadXML(xmlFile) {
xmlDoc.async = "false";
xmlDoc.onreadystatechange = verify;
xmlDoc.load(xmlFile);
}
function verify() {
if(xmlDoc.readyState != 4) {
return false;
}
}
function traverse(tree) {
if(tree.hasChildNodes()) {
document.write('<ul><li>');
document.write('<b>' + tree.tagName + ': </b>');
var nodes = tree.childNodes.length;
for(var i = 0; i < tree.childNodes.length; i++) {
traverse(tree.childNodes(i));
}
document.write('</il></ul>');
} else {
document.write(tree.text);
}
}
function initTraverse(file) {
loadXML(file);
var doc = xmlDoc.documentElement;
traverse(doc);
}
When I fired Safari I saw that nothing was displayed, then I've opened the Error Console and what I got was this:
ReferenceError: Can't find variable: ActiveXObject
What should I do to make this work?
PS: I would prefer if this page could be capable of running at Mobile Safari
ActiveXObject do not work outside of internet explorer.
There are a few alternative xml parser's and handlers like E4X. Although E4X is currently only done in firefox (https://developer.mozilla.org/En/E4X/Processing_XML_with_E4X).
If using jQuery is an option then you can look into marcgrabanski.com/articles/jquery-makes-parsing-xml-easy
Some interesting stuff going on there. Most interesting is the async = false line. You probably want to re-consider that bit. In order to change to an asynchronous request, you would have to re-write some other code and remove the document.write calls.
Regardless, here is a (untested but hopefully) drop in replacement for what you have using XMLHttpRequest instead of an xml document.
var xmlDoc = null;
function loadXML(xmlFile) {
var request = new XMLHttpRequest();
request.open('GET', xmlFile, false); // false is synchronous
request.send();
xmlDoc = request.responseXML;
}
You may have to do some debugging...
You should have something cross-browser compatible with either DOMParser or DOMDocument. Of course, I'm not sure if you're wanting to parse a XML URL or a XML string. For a XML URL, I recommend:
if (window.XMLHttpRequest) return new window.XMLHttpRequest();
else if (window.ActiveXObject) {
// the many versions of IE's XML fetchers
var AXOs = [
'MSXML2.XMLHTTP.6.0',
'MSXML2.XMLHTTP.5.0',
'MSXML2.XMLHTTP.4.0',
'MSXML2.XMLHTTP.3.0',
'MSXML2.XMLHTTP',
'Microsoft.XMLHTTP',
'MSXML.XMLHTTP'
];
for (var i = 0; i < AXOs.length; i++) {
try { return new ActiveXObject(AXOs[i]); }
catch() { continue; }
}
return null;
}
For a XML string, this code block would work better:
if (window.DOMParser) return (new DOMParser()).parseFromString(str, 'text/xml');
else if (window.ActiveXObject) {
var doc;
// the many versions of IE's DOM parsers
var AXOs = [
'MSXML2.DOMDocument.6.0',
'MSXML2.DOMDocument.5.0',
'MSXML2.DOMDocument.4.0',
'MSXML2.DOMDocument.3.0',
'MSXML2.DOMDocument',
'Microsoft.XMLDOM',
'MSXML.DOMDocument'
];
for (var i = 0; i < AXOs.length; i++) {
try { doc = new ActiveXObject(AXOs[i]); break; }
catch() { continue; }
}
if (!doc) return createElement('div', null);
if (doc.async) doc.async = false;
doc.loadXML(str);
return doc;
}
return createElement('div', null);
The DOMDocument objects do support a load() method for loading XML from a URL, but it's a different syntax than the XMLHttpRequest and XMLHTTP methods.
The DOMDocument appears (at least from the MSDN docs) to also contain the XMLHTTP methods, so you could interlace DOMDocument in the AXOs array, but I'm not certain about that. Plus, I can't imagine DOMDocument being in place without XMLHTTP.

Using Jquery to return elements of XML string doesn't work in Internet Explorer 9

Using jQuery I use the selector and the each function to itterate through named elements of an XML string.
e.g.
$("<xml><elem></elem><elem></elem></xml>").each(function()
{
alert("processing elem tag");
});
This works fine in FF/Chrome/IE<8 but in 9 fails. Presumably something in the IE doc no longer allows this.
It's not intended to take a string of XML directly, only from an AJAX response, e.g. .responseXML, in any case don't worry about it at this point.
IE9 has bugs, it's not RTM quality, they are mostly their bugs...I personally wouldn't waste time changing (or even debugging) your code until their's is more complete/stable. (Opinion) don't worry about the client with IE9 either...they signed on for a buggy experience when they installed pre-release software.
jQuery doesn't parse XML. What $("<xml><elem></elem><elem></elem></xml>") does is to create an element and set its innerHTML property to "<xml><elem></elem><elem></elem></xml>", which will have variable and unpredictable results.
You need to parse the XML using the browser's built-in XML parser. Here's a function that does this. I haven't tested it in IE 9 but I'd be surprised if it didn't work: they've implemented DOMParser, so unlike IE < 9 it will fall into the first branch and should work unless they've made a mess of it.
var parseXml;
if (window.DOMParser) {
parseXml = function(xmlStr) {
return ( new window.DOMParser() ).parseFromString(xmlStr, "text/xml");
};
} else if (typeof window.ActiveXObject != "undefined" && new window.ActiveXObject("Microsoft.XMLDOM")) {
parseXml = function(xmlStr) {
var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = "false";
xmlDoc.loadXML(xmlStr);
return xmlDoc;
};
} else {
parseXml = function() { return null; }
}
var xmlStr = "<xml><elem></elem><elem></elem></xml>";
var xmlDoc = parseXml(xmlStr);
$(xmlDoc).each(function() {
alert("processing elem tag");
});

Categories