Reputation: 433
I am trying to flatten a tree of nested XML elements by combining the values of an attribute using XSLT. For instance, if I have the following input:
<node value="a">
<node value="b">
<node value="c">
<node value="d">
</node>
</node>
<node value="e">
<node value="f">
</node>
<node value="g">
<node value="h">
</node>
</node>
</node>
</node>
</node>
Then these would be the "flattened" results I would like to be able to get:
a/b/c/d
a/b/e/f
a/b/e/g/h
All I currently have been able to achieve is outputting a record only on the most deeply nested occurrences of node "node" with the "value" attribute:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:csv="csv:csv">
<xsl:output method="text" encoding="utf-8" />
<xsl:template match="text()|@*"/>
<xsl:template match="node[@value]">
<xsl:if test="not(descendant::node[@value])">
<xsl:value-of select="@value"/>
<xsl:text>
</xsl:text>
</xsl:if>
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
As you may have gathered from my description and the xsl:if
test, a potential complication is that some instances of the "node" element may not have the "value" attribute and so this must be explicitly checked. How can I update this stylesheet to achieve the desired result?
Upvotes: 1
Views: 451
Reputation: 167716
With XSLT 2 or 3 it becomes
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:value-of select="descendant::node[@value and not(descendant::node[@value])]!string-join(ancestor-or-self::node/@value, '/')" separator=" "/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/b4GWVh/0
With XSLT 1 I would use
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:apply-templates select="descendant::node[@value and not(descendant::node[@value])]"/>
</xsl:template>
<xsl:template match="node">
<xsl:apply-templates select="ancestor-or-self::node/@value"/>
<xsl:text> </xsl:text>
</xsl:template>
<xsl:template match="@value">
<xsl:if test="position() > 1">/</xsl:if>
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/b4GWVh/1
Upvotes: 2