I am a big user of public XML formatters, such as http://www.xmlformatter.net/ as part of my regular work, largely because I need to understand some glob of an XML returned without any formatting by a server presumably tuned for maximum performance.
More recently, I was faced with the need to produce those nice XML representation within automatically generated documentation, specially now that version 0.9.0 of the excellent Lunatech Jaxdoclets library (http://www.lunatech-labs.com/open-source/jax-doclets) can also include external HTML contents in the final jaxdoc output of a JAX-RS implementation.
So what I needed was a formatter that could be embedded into our build structure, or even incorporated into a live server to create a private XML formatter service where we did not have to worry about obfuscating XML contents before sending them out to the extranet (e.g. IP addresses, passwords, someone’s address, etc) .
Since our build is based on Ant and our live server based on Tomcat, it made sense to settle into using XSLT. This link (http://www.dpawson.co.uk/xsl/sect2/pretty.html) is a good start, but I really wanted output in HTML with all the niceties of CSS styles, etc.
For now, the transformation is listed below. I am not terribly happy with the solution for namespaces and the “pad” template would need a serious performance optimization if used in mainline processing (not my case) , probably by a sub-string function call using a long chunk of whitespaces as parameters since XSLT does not have generic for loops.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:xs="http://www.w3.org/2001/XMLSchema" >
<xsl:output method="html" />
<xsl:strip-space elements="*" />
<!--
- Root match
-->
<xsl:template match="/">
<html>
<head>
<title>HTML version of XML resource</title>
<link rel="stylesheet" type="text/css" href="frs-jaxdoc.css" />
</head>
<body>
<xsl:apply-templates>
<xsl:with-param name="indent" select="0" />
</xsl:apply-templates>
</body>
</html>
</xsl:template>
<!--
- Processes XML elements
-
- param indent indentation count
- param ancestorsNamespaces concatenation of all namespace URIs from all parents, which is
- used to determine if a namespace has already been declared in
- a parent XML element.
-->
<xsl:template match="node()[name()]">
<xsl:param name="indent" select="0" />
<xsl:param name="ancestorsNamespaces" select="''" />
<xsl:variable name="hasChildren" select="count(child::node()) > 0"/>
<xsl:variable name="hasTextNode" select="count(child::text()) > 0"/>
<!-- Format opening of XML element -->
<xsl:if test="$indent > 0"><br/>
</xsl:if>
<xsl:call-template name="pad">
<xsl:with-param name="indent" select="$indent * 2" />
</xsl:call-template>
<span class="xmlElement"><<xsl:value-of select="name()" /></span>
<!-- Add namespaces to XML element -->
<xsl:variable name="selfNamespaces">
<xsl:for-each select="namespace::node()">
<xsl:value-of select="." />
</xsl:for-each>
</xsl:variable>
<xsl:variable name="hasAttributes" select="count(@*) > 0" />
<xsl:for-each select="namespace::node()">
<xsl:if test="contains($ancestorsNamespaces, .) = false()">
<xsl:if test="position() = 1"> </xsl:if>
<xsl:if test="position() > 1">
<br/>
<xsl:call-template name="pad">
<xsl:with-param name="indent" select="($indent+1) * 2" />
</xsl:call-template>
</xsl:if>
<span class="xmlNamespacePrefix">xmlns:<xsl:value-of select="name()" /></span>=<span class="xmlNamespaceUri">"<xsl:value-of select="." />"</span>
<xsl:if test="position()=last() and $hasAttributes">
<br/>
<xsl:call-template name="pad">
<xsl:with-param name="indent" select="($indent+1) * 2" />
</xsl:call-template>
</xsl:if>
</xsl:if>
</xsl:for-each>
<!-- Add attributes to XML element -->
<xsl:apply-templates select="@*" />
<!-- Close opening XML element tag, with special handling for element without children node -->
<span class="xmlElement"><xsl:if test="$hasChildren = false()">/</xsl:if>></span>
<!-- Add children to XML element -->
<xsl:apply-templates select="node()">
<xsl:with-param name="indent" select="$indent + 1" />
<xsl:with-param name="ancestorsNamespaces" select="concat($ancestorsNamespaces,$selfNamespaces)" />
</xsl:apply-templates>
<!-- Close XML element, but only if not already closed with XML element abbreviation -->
<xsl:if test="$hasChildren = true()">
<xsl:if test="$hasTextNode = false()">
<br/>
<xsl:call-template name="pad">
<xsl:with-param name="indent" select="$indent * 2" />
</xsl:call-template>
</xsl:if>
<span class="xmlElement"></<xsl:value-of select="name()" />></span>
</xsl:if>
</xsl:template>
<!--
- XML text
-->
<xsl:template match="text()">
<xsl:variable name="nodeValue" select="." />
<xsl:if test="string-length($nodeValue) > 0">
<span class="xmlText"><xsl:copy-of select="$nodeValue" /></span>
</xsl:if>
</xsl:template>
<!--
- XML attributes
-->
<xsl:template match="@*">
<xsl:if test="position() > 0"> </xsl:if>
<span class="xmlAttr"> <xsl:value-of select="name()" /> </span>=<span class="xmlAttrValue">"<xsl:value-of select="." />"</span>
</xsl:template>
<!--
- Padding for HTML output, used for indentation purposes
-->
<xsl:template name="pad">
<xsl:param name="indent" select="0" />
<xsl:if test="$indent > 0">
 
<xsl:call-template name="pad">
<xsl:with-param name="indent" select="$indent - 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
And this is the companion CSS file:
.xmlElement {
color: #990099;
}
.xmlAttr {
color: #660066;
}
.xmlAttrValue {
color: #0000CC
}
.xmlNamespacePrefix {
color: #666600;
}
.xmlNamespaceUri {
color: #000099
}
xmlText {
}
BODY {
font-size: 10pt;
font-family: "Courier New"
}
The style names for classes are not that verbose, but I thought I would make it more legible for this entry.

