Pretty printing XML with javascript Pretty printing XML with javascript xml xml

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>

JSFiddle

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;    };