Reputation: 2282
Given the following XML:
<package>
<node name="a">
<node name="b"/>
<node name="c"/>
<node name="d">
<node name="e"/>
<node name="f"/>
<node name="g">
<node name="h"/>
</node>
</node>
</node>
</package>
I basically want to flatten the tree while concatenating the name
attributes of each parent node
element until the last node
element:
<package>
<node name="a-b"/>
<node name="a-c"/>
<node name="a-d-e"/>
<node name="a-d-f"/>
<node name="a-d-g-h"/>
</package>
What I got working so far is is properly generating a flat list of all node
elements using a template and xsl:copy-of
:
<xsl:template match="//node">
<xsl:copy-of select="current()"/>
</xsl:template>
This gives me:
<package>
<node name="b"/>
<node name="c"/>
<node name="e"/>
<node name="f"/>
<node name="h"/>
</package>
But I'm not sure how to properly continue from here. My intention was to extend the template and using xsl:attribute
and xsl:for-each
to concatenate and modify the attribute:
<xsl:template match="node/@name">
<xsl:attribute name="name">
<xsl:for-each select="ancestor::node">
<xsl:if test="position() > 1">.</xsl:if>
<xsl:value-of select="@name"/>
</xsl:for-each>
</xsl:attribute>
</xsl:template>
However, this only prints the node
's data (if any).
What am I missing here?
I have XSLT 2.0 available and I got my inspiration from this SO question.
Upvotes: 1
Views: 362
Reputation: 243479
I basically want to flatten the tree while concatenating the name attributes of each parent node element until the last node element:
Here is a complete and working XSLT 2.0 solution:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node[*]"><xsl:apply-templates/></xsl:template>
<xsl:template match="node[not(*)]/@name">
<xsl:attribute name="name" select="string-join(../ancestor-or-self::node/@name, '-')"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<package>
<node name="a">
<node name="b"/>
<node name="c"/>
<node name="d">
<node name="e"/>
<node name="f"/>
<node name="g">
<node name="h"/>
</node>
</node>
</node>
</package>
the wanted, correct result is produced:
<package>
<node name="a-b"/>
<node name="a-c"/>
<node name="a-d-e"/>
<node name="a-d-f"/>
<node name="a-d-g-h"/>
</package>
II. XSLT 1.0 solution:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node[*]"><xsl:apply-templates/></xsl:template>
<xsl:template match="node[not(*)]/@name">
<xsl:attribute name="name"><xsl:apply-templates select="." mode="gen"/></xsl:attribute>
</xsl:template>
<xsl:template match="node/@name" mode="gen">
<xsl:apply-templates select="../parent::node/@name" mode="gen"/>
<xsl:if test="../parent::node">-</xsl:if>
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the same XML document (above), the same correct result is produced:
<package>
<node name="a-b"/>
<node name="a-c"/>
<node name="a-d-e"/>
<node name="a-d-f"/>
<node name="a-d-g-h"/>
</package>
Upvotes: 1
Reputation: 167571
Use string-join
:
<xsl:template match="node">
<node name="{string-join(ancestor-or-self::node/@name, '-')}"/>
</xsl:template>
Upvotes: 1