Reputation: 89
I have the following XML:
<entry>
<form type="variant">
<form type="hyperlemma">
<orth>wordA</orth>
</form>
<orth>wordB</orth>
<gramGrp>
<gram>vb</gram>
<gram>inf</gram>
</gramGrp>
<cit>
<form type="lemma">
<orth>wordC</orth>
</form>
</cit>
<form type="graphical_variant">
<form type="hyperlemma">
<orth>wordD</orth>
</form>
<orth>wordE</orth>
<cit>
<orth>∅</orth>
</cit>
</form>
</form>
</entry>
Child nodes of <form type="variant">
- namely <orth>wordB</orth>
and <cit>
- are supposed to go in a second <form type="graphical_variant">
that is newly added right before the first one.
The desired output is
<entry>
<form type="variant">
<form type="hyperlemma">
<orth>wordA</orth>
</form>
<gramGrp>
<gram>vb</gram>
<gram>inf</gram>
</gramGrp>
<form type="graphical_variant">
<orth>wordB</orth>
<cit>
<form type="lemma">
<orth>wordC</orth>
</form>
</cit>
</form>
<form type="graphical_variant">
<form type="hyperlemma">
<orth>wordD</orth>
</form>
<orth>wordE</orth>
<cit>
<orth>∅</orth>
</cit>
</form>
</form>
</entry>
Applying this XSLT, templates 1 and 2 are doing the copying
<!--TEMPLATE1-->
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*" />
</xsl:copy>
</xsl:template>
<!--TEMPLATE2-->
<xsl:template match="form[@type='graphical_variant']">
<form type="graphical_variant">
<xsl:apply-templates select="../orth" />
<!--in case there are several <cit> elements-->
<xsl:for-each select="../cit">
<xsl:apply-templates select="." />
</xsl:for-each>
</form>
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<!--TEMPLATE3-->
<xsl:template match="form[@type='variant']/cit" />
<!--TEMPLATE4-->
<xsl:template match="form[@type='variant']/orth"/>
But as soon as I try to delete those copied elements from <form type="variant">
using templates 3 and 4, they are deleted from the newly created element <form type="graphical_variant">
, too.
This may be a stupid question, but why is that so? By referring to form[@type='variant']
(template3), I thought <cit>
was going to be deleted there only. I'd be glad if someone could explain what I'm doing wrong - and perhaps even provide a solution!
I forgot to mention: it is XSLT 2.0.
Upvotes: 0
Views: 1087
Reputation: 116992
The problem with your approach is that when you do:
<xsl:apply-templates select="../orth" />
you are applying templates to orth
elements that are siblings of the current node - i.e. children of the <form type="variant">
element. There's only template that matches these orth
elements - it's your TEMPLATE4, and that template is empty.
Same thing happens with your:
<xsl:for-each select="../cit">
<xsl:apply-templates select="." />
</xsl:for-each>
That's just an awkward way of saying:
<xsl:apply-templates select="../cit" />
which applies the empty TEMPLATE3 to these nodes.
How to fix this:
There are three ways you could approach this:
Instead of applying templates, copy the nodes to where you want them:
<!--TEMPLATE1-->
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*" />
</xsl:copy>
</xsl:template>
<!--TEMPLATE2-->
<xsl:template match="form[@type='graphical_variant']">
<form type="graphical_variant">
<xsl:copy-of select="../orth" />
<xsl:copy-of select="../cit" />
</form>
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<!--TEMPLATE3-->
<xsl:template match="form[@type='variant']/cit" />
<!--TEMPLATE4-->
<xsl:template match="form[@type='variant']/orth"/>
Apply different templates (using mode) when you want the nodes to be copied.
Be more selective when applying the templates (this, IMHO, is the preferable approach):
XSLT 2.0
<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="form[@type='variant']">
<xsl:copy>
<!-- remove orth and cit from here -->
<xsl:apply-templates select="@*|node() except (orth|cit)"/>
</xsl:copy>
</xsl:template>
<xsl:template match="form[@type='graphical_variant']">
<form type="graphical_variant">
<!-- add orth and cit here -->
<xsl:apply-templates select="../orth" />
<xsl:apply-templates select="../cit" />
</form>
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Upvotes: 2
Reputation: 19313
<xsl:apply-templates select="." />
Here you go, it matches to empty templates towards the bottom of your example.
Simple, however not optimal solution would be
<xsl:apply-templates select="." mode="noignore" />
And then make 's that match your desired elements with mode="noignore" and actually copy them instead of suppressing.
Upvotes: 0