Pretty printing XML with javascript
From the text of the question I get the impression that a string result is expected, as opposed to an HTML-formatted result.
If this is so, the simplest way to achieve this is to process the XML document with the identity transformation and with an <xsl:output indent="yes"/>
instruction:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template></xsl:stylesheet>
When applying this transformation on the provided XML document:
<root><node/></root>
most XSLT processors (.NET XslCompiledTransform, Saxon 6.5.4 and Saxon 9.0.0.2, AltovaXML) produce the wanted result:
<root> <node /></root>
This can be done using native javascript tools, without 3rd party libs, extending the @Dimitre Novatchev's answer:
var prettifyXml = function(sourceXml){ var xmlDoc = new DOMParser().parseFromString(sourceXml, 'application/xml'); var xsltDoc = new DOMParser().parseFromString([ // describes how we want to modify the XML - indent everything '<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">', ' <xsl:strip-space elements="*"/>', ' <xsl:template match="para[content-style][not(text())]">', // change to just text() to strip space in text nodes ' <xsl:value-of select="normalize-space(.)"/>', ' </xsl:template>', ' <xsl:template match="node()|@*">', ' <xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy>', ' </xsl:template>', ' <xsl:output indent="yes"/>', '</xsl:stylesheet>', ].join('\n'), 'application/xml'); var xsltProcessor = new XSLTProcessor(); xsltProcessor.importStylesheet(xsltDoc); var resultDoc = xsltProcessor.transformToDocument(xmlDoc); var resultXml = new XMLSerializer().serializeToString(resultDoc); return resultXml;};console.log(prettifyXml('<root><node/></root>'));
Outputs:
<root> <node/></root>
Note, as pointed out by @jat255, pretty printing with <xsl:output indent="yes"/>
is not supported by firefox. It only seems to work in chrome, opera and probably the rest webkit-based browsers.
Slight modification of efnx clckclcks's javascript function. I changed the formatting from spaces to tab, but most importantly I allowed text to remain on one line:
var formatXml = this.formatXml = function (xml) { var reg = /(>)\s*(<)(\/*)/g; // updated Mar 30, 2015 var wsexp = / *(.*) +\n/g; var contexp = /(<.+>)(.+\n)/g; xml = xml.replace(reg, '$1\n$2$3').replace(wsexp, '$1\n').replace(contexp, '$1\n$2'); var pad = 0; var formatted = ''; var lines = xml.split('\n'); var indent = 0; var lastType = 'other'; // 4 types of tags - single, closing, opening, other (text, doctype, comment) - 4*4 = 16 transitions var transitions = { 'single->single': 0, 'single->closing': -1, 'single->opening': 0, 'single->other': 0, 'closing->single': 0, 'closing->closing': -1, 'closing->opening': 0, 'closing->other': 0, 'opening->single': 1, 'opening->closing': 0, 'opening->opening': 1, 'opening->other': 1, 'other->single': 0, 'other->closing': -1, 'other->opening': 0, 'other->other': 0 }; for (var i = 0; i < lines.length; i++) { var ln = lines[i]; // Luca Viggiani 2017-07-03: handle optional <?xml ... ?> declaration if (ln.match(/\s*<\?xml/)) { formatted += ln + "\n"; continue; } // --- var single = Boolean(ln.match(/<.+\/>/)); // is this line a single tag? ex. <br /> var closing = Boolean(ln.match(/<\/.+>/)); // is this a closing tag? ex. </a> var opening = Boolean(ln.match(/<[^!].*>/)); // is this even a tag (that's not <!something>) var type = single ? 'single' : closing ? 'closing' : opening ? 'opening' : 'other'; var fromTo = lastType + '->' + type; lastType = type; var padding = ''; indent += transitions[fromTo]; for (var j = 0; j < indent; j++) { padding += '\t'; } if (fromTo == 'opening->closing') formatted = formatted.substr(0, formatted.length - 1) + ln + '\n'; // substr removes line break (\n) from prev loop else formatted += padding + ln + '\n'; } return formatted; };