I have an XML document of the following kind:
<item>
<item>
<item>
... (same elements <item> ... </item> here)
</item>
</item>
</item>
... and the following XSL-transform:
<xsl:template match="item"><xsl:text>
open</xsl:text>
<xsl:apply-templates/><xsl:text>
close</xsl:text>
</xsl:template>
What I obtain is:
open
open
open
close
close
close
So I wonder whether it is possible to somehow get an output with indents like this:
open
open
open
close
close
close
Thanks for your help!
P.S. It should be definitely possible to obtain what I want to by letting output method of the transformation to be HTML. However, I need to make indents "directly" in the text, not using any kind of HTML's lists etc.
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="item">
<xsl:param name="pIndent" select="' '"/>
<xsl:value-of select="concat('
', $pIndent, 'open')"/>
<xsl:apply-templates>
<xsl:with-param name="pIndent"
select="concat($pIndent, ' ')"/>
</xsl:apply-templates>
<xsl:value-of select="concat('
', $pIndent, 'close')"/>
</xsl:template>
<xsl:template match="node()[not(self::item)]">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<item>
<item>
<item> ...
<item> ... </item>
</item>
</item>
</item>
produces the wanted, indented output:
open
open
open
open
close
close
close
close
Explanation:
The $pIndent
parameter is used to hold the string of whitespace to be prepended to the non-white space output. Whenever an xsl:apply-templates
is used, the value passed with this parameter is expanded by two spaces.
It's simple, just use tabs inside you <xsl:text>
elements (I don't know how to put them here):
<xsl:template match="item"><xsl:text>
open</xsl:text>
However, to achieve the indented structure you would need to create a named template or xpath function that outputs several times the tab (or spaces).
There's a 'cheat' way of doing this using substring. To modify the template you already have:
<xsl:template match="item">
<xsl:variable name="indent" select="substring(' ',1,count(ancestor::*)*2)" />
<xsl:text> </xsl:text>
<xsl:value-of select="$indent" />
<xsl:text>open</xsl:text>
<xsl:apply-templates/>
<xsl:text> </xsl:text>
<xsl:value-of select="$indent" />
<xsl:text>close</xsl:text>
</xsl:template>
As you can see, it simply inserts a number of spaces based on how many ancestors the element has (multiplied by 2), by taking a portion of a string of spaces. I've used 10 here, which means it'll stop indenting at 5 levels, but you can use simply use a longer string if your XML is deeper than that.
It also has the advantage that you can do custom indenting quite easily, by using a different string. You could use 1-2-3-4-5-6-7-8-9-
for example, if you wanted to show clearly how indented each line is.
I also replaced the carriage returns with
, just to make the code easier to indent for readability.