XSL repeat the parent node for each child node
For each child node, I want to duplicate my parent node so that the resulting xml, contains only one child for the parent node with the other nodes being the same.
Here is a sample input
<a>
<a1>header1</a开发者_如何学Python1>
<a2>header2</a2>
<a3>
<a31>
<a311>line_1</a311>
<a311>line_2</a311>
</a31>
<a32>5o$</a32>
<a33>Add</a33>
</a3>
<a4>account_holder</a4>
</a>
What I want to do is - repeat a3 for as many times as the node a311 comes. Rest all nodes are retained
Output
<a>
<a1>header1</a1>
<a2>header2</a2>
<a3>
<a31>
<a311>line_1</a311>
</a31>
<a32>5o$</a32>
<a33>Add</a33>
</a3>
<a3>
<a31>
<a311>line_2</a311>
</a31>
<a32>5o$</a32>
<a33>Add</a33>
</a3>
<a4>account_holder</a4>
</a>
More semantic with "tunnel param" pattern, this stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()" name="identity">
<xsl:param name="pCurrent"/>
<xsl:copy>
<xsl:apply-templates select="@*|node()">
<xsl:with-param name="pCurrent" select="$pCurrent"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="a3">
<xsl:variable name="vCurrent" select="."/>
<xsl:variable name="vDescendants" select=".//a311"/>
<xsl:for-each select="$vDescendants|$vCurrent[not($vDescendants)]">
<xsl:variable name="vDescendant" select="."/>
<xsl:for-each select="$vCurrent">
<xsl:call-template name="identity">
<xsl:with-param name="pCurrent" select="$vDescendant"/>
</xsl:call-template>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
<xsl:template match="a311">
<xsl:param name="pCurrent"/>
<xsl:if test="generate-id()=generate-id($pCurrent)">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Output:
<a>
<a1>header1</a1>
<a2>header2</a2>
<a3>
<a31>
<a311>line_1</a311>
</a31>
<a32>5o$</a32>
<a33>Add</a33>
</a3>
<a3>
<a31>
<a311>line_2</a311>
</a31>
<a32>5o$</a32>
<a33>Add</a33>
</a3>
<a4>account_holder</a4>
</a>
EDIT: Handling no descendants case.
The following (XSLT 1.0) stylesheet produces the desired result:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="a3">
<xsl:apply-templates select="a31/a311" />
</xsl:template>
<xsl:template match="a311">
<a3>
<a31>
<a311>
<xsl:value-of select="." />
</a311>
</a31>
<xsl:apply-templates select="../../*[not(self::a31)]" />
</a3>
</xsl:template>
</xsl:stylesheet>
You didn't specify the XSLT version. Here's an XSLT2 stylesheet that will accomplish what you want:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="a3" mode="#default">
<xsl:apply-templates select="a31/a311"/>
</xsl:template>
<xsl:template match="a311">
<xsl:apply-templates select="../.." mode="x">
<xsl:with-param name="label" select="text()"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="a3" mode="x">
<xsl:param name="label"/>
<xsl:copy>
<xsl:apply-templates select="@*"/>
<a31>
<a311><xsl:value-of select="$label"/></a311>
</a31>
<xsl:apply-templates select="* except a31"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
It uses an identity transform to pass through the "uninteresting" elements while trapping the <a3>
node and applying special handling. If you need an XSLT1 solution, merely
- Replace
select="* except a31"
withselect="*[name() != 'a31']"
- Remove the
mode="#default"
in the first "a3" template
精彩评论