Reputation: 65
I need to copy everything to new XML except some elements which I need to rename.
All "child" nodes which are childs of "node1" I need to rename to "children" and "child" nodes which are childs of "node2" I need to rename to "kid".
Perfect solution will be using
match="/root/node1/descendant::child"
But as I found in Michael Kay response in topic http://comments.gmane.org/gmane.text.xml.saxon.help/14956
A pattern of the form /descendant::m__id[1] is not legal in either XSLT 1.0 or XSLT 2.0, though it becomes legal in XSLT 3.0.
Do You have any suggestions how to do that in XSLT 2.0 without doing what I made below? Because I don't know how many nesting there will be exactly.
Here is my xml example
<?xml version="1.0" encoding="UTF-8"?>
<root>
<node1>
<child>
<subnode>
<child></child>
</subnode>
</child>
<child>
<subnode>
<child></child>
</subnode>
</child>
</node1>
<node2>
<child>
<subnode>
<child></child>
</subnode>
</child>
<child>
<subnode>
<child></child>
</subnode>
</child>
</node2>
</root>
And XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.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="/root/node1/child">
<xsl:element name="children">
<xsl:apply-templates select="@*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="/root/node1/child/subnode/child">
<xsl:element name="children">
<xsl:apply-templates select="@*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="/root/node2/child">
<xsl:element name="kid">
<xsl:apply-templates select="@*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="/root/node2/child/subnode/child">
<xsl:element name="kid">
<xsl:apply-templates select="@*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1
Views: 695
Reputation: 243579
This solution doesn't use any predicates or axes at all, has the shortest match patterns, and thus is more efficient than one that uses predicates.
It also produces the expected correct result when <node1>
has descendents or ancestors <node2>
-- something which the remaining solutions don't handle at all.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()|@*" mode="#all">
<xsl:copy>
<xsl:apply-templates select="node()|@*" mode="#current"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node1" mode="#all">
<xsl:copy>
<xsl:apply-templates select="node()|@*" mode="node1"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node2" mode="#all">
<xsl:copy>
<xsl:apply-templates select="node()|@*" mode="node2"/>
</xsl:copy>
</xsl:template>
<xsl:template match="child" mode="node1">
<children>
<xsl:apply-templates select="node()|@*" mode="#current"/>
</children>
</xsl:template>
<xsl:template match="child" mode="node2">
<kid>
<xsl:apply-templates select="node()|@*" mode="#current"/>
</kid>
</xsl:template>
</xsl:stylesheet>
When applied on the provided XML document:
<root>
<node1>
<child>
<subnode>
<child></child>
</subnode>
</child>
<child>
<subnode>
<child></child>
</subnode>
</child>
</node1>
<node2>
<child>
<subnode>
<child></child>
</subnode>
</child>
<child>
<subnode>
<child></child>
</subnode>
</child>
</node2>
</root>
the wanted, correct result is produced:
<root>
<node1>
<children>
<subnode>
<children/>
</subnode>
</children>
<children>
<subnode>
<children/>
</subnode>
</children>
</node1>
<node2>
<kid>
<subnode>
<kid/>
</subnode>
</kid>
<kid>
<subnode>
<kid/>
</subnode>
</kid>
</node2>
</root>
When the same transformation is applied on this XML document, where <node1>
and <node2>
are descendents or ancestors of each other:
<root>
<node1>
<child>
<subnode>
<node2>
<child></child>
<node1>
<child/>
</node1>
</node2>
</subnode>
</child>
<child>
<subnode>
<child></child>
</subnode>
</child>
</node1>
<node2>
<child>
<subnode>
<child></child>
</subnode>
</child>
<node1>
<child>
<subnode>
<child></child>
</subnode>
</child>
</node1>
<child>
<subnode>
<child></child>
</subnode>
</child>
</node2>
</root>
again the correct and wanted result (the replacement-name of <child>
is determined by its closest ancestor that is either <node1>
or <node2>
) is produced:
<root>
<node1>
<children>
<subnode>
<node2>
<kid/>
<node1>
<children/>
</node1>
</node2>
</subnode>
</children>
<children>
<subnode>
<children/>
</subnode>
</children>
</node1>
<node2>
<kid>
<subnode>
<kid/>
</subnode>
</kid>
<node1>
<children>
<subnode>
<children/>
</subnode>
</children>
</node1>
<kid>
<subnode>
<kid/>
</subnode>
</kid>
</node2>
</root>
Upvotes: 1
Reputation: 167716
You can use //child
in a pattern:
<xsl:template match="/root/node1//child">
<xsl:element name="children">
<xsl:apply-templates select="@*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="/root/node2//child">
<xsl:element name="kid">
<xsl:apply-templates select="@*|node()"/>
</xsl:element>
</xsl:template>
I would use literal result elements instead of xsl:element:
<xsl:template match="/root/node1//child">
<children>
<xsl:apply-templates select="@*|node()"/>
</children>
</xsl:template>
<xsl:template match="/root/node2//child">
<kid>
<xsl:apply-templates select="@*|node()"/>
</kid>
</xsl:template>
Upvotes: 1
Reputation: 117140
Would this work for you?
<xsl:stylesheet version="2.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="*"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="child">
<children>
<xsl:apply-templates select="@*|node()"/>
</children>
</xsl:template>
<xsl:template match="child[ancestor::node2]">
<kid>
<xsl:apply-templates select="@*|node()"/>
</kid>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1