Reputation: 1288
At the moment I'm struggeling to get my XSLT working properly. I trying to transform a XML into a unordered list with the children inside a sub unordered list. The problem is that XSL creates empty list elements if a person doesn't have a child.
XML:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="Trees.xsl"?>
<Tree>
<Person ID="1" FirstName="test" LastName="" ChildOf="0" />
<Person ID="2" FirstName="test2" LastName="" ChildOf="1" />
<Person ID="3" FirstName="test3" LastName="" ChildOf="1" />
<Person ID="4" FirstName="test4" LastName="" ChildOf="1" />
<Person ID="6" FirstName="test6" LastName="" ChildOf="3" />
<Person ID="5" FirstName="test5" LastName="" ChildOf="0" />
</Tree>
XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="Person">
<ul><li><xsl:value-of select="@FirstName" /></li>
<li><xsl:apply-templates select="../Person[@ChildOf=current()/@ID]" />
</li></ul>
</xsl:template>
</xsl:stylesheet>
I already tried checking with an if if the FirstName is empty or not, but it doesn't work. Where is the error.
Upvotes: 1
Views: 242
Reputation: 243539
A simpler, shorter and efficient solution (no variables and no conditional instructions):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kPersById" match="Person" use="@ID"/>
<xsl:key name="kChildrenOf" match="Person" use="@ChildOf"/>
<xsl:template match="/*">
<xsl:apply-templates select="Person[not(key('kPersById', @ChildOf))][1]" mode="start"/>
</xsl:template>
<xsl:template match="Person" mode="start">
<ul>
<li><xsl:value-of select="@FirstName" /></li>
<xsl:apply-templates select="key('kChildrenOf', @ID)[1]" mode="start"/>
<xsl:apply-templates select="key('kChildrenOf', @ChildOf)[position() > 1]"/>
</ul>
</xsl:template>
<xsl:template match="Person">
<li><xsl:value-of select="@FirstName" /></li>
<xsl:apply-templates select="key('kChildrenOf', @ID)[1]" mode="start"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<Tree>
<Person ID="1" FirstName="test" LastName="" ChildOf="0" />
<Person ID="2" FirstName="test2" LastName="" ChildOf="1" />
<Person ID="3" FirstName="test3" LastName="" ChildOf="1" />
<Person ID="4" FirstName="test4" LastName="" ChildOf="1" />
<Person ID="6" FirstName="test6" LastName="" ChildOf="3" />
<Person ID="5" FirstName="test5" LastName="" ChildOf="0" />
</Tree>
the wanted, correct result is produced:
<ul>
<li>test</li>
<ul>
<li>test2</li>
<li>test3</li>
<ul>
<li>test6</li>
</ul>
<li>test4</li>
</ul>
<li>test5</li>
</ul>
Do note:
This solution doesn't depend on the top-level Person
elements having ChildOf="0"
. In fact, for the top-level elements this attribute can be completely omitted or different top elements could have ChildOf
attributes with different values -- such as when these have been "cut" from another, bigger hierarchy.
Upvotes: 1
Reputation: 117103
I would suggest you try it this way:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="children" match="Person" use="@ChildOf" />
<xsl:template match="/Tree">
<ul>
<xsl:apply-templates select="Person[@ChildOf=0]"/>
</ul>
</xsl:template>
<xsl:template match="Person">
<li>
<xsl:value-of select="@FirstName" />
</li>
<xsl:variable name="children" select="key('children', @ID)" />
<xsl:if test="$children">
<ul>
<xsl:apply-templates select="$children" />
</ul>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Upvotes: 3