Reputation: 1872
I have this XML:
<?xml version="1.0" encoding="UTF-8"?>
<document>
<shortbody>
<text>
testing
</text>
<text>
shortbody
</text>
</shortbody>
<body>
<paragraph>
<text>
A new version of xsltransform.net is released!
</text>
</paragraph>
<paragraph>
<text>
We have added the following new features:
</text>
</paragraph>
</body>
</document>
And this XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="body">
<result>
<p>
<xsl:apply-templates select="//shortbody" />
</p>
<xsl:for-each select="paragraph">
<xsl:element name="paragraph">
<xsl:apply-templates select="."/>
</xsl:element>
</xsl:for-each>
</result>
</xsl:template>
<xsl:template match="body/paragraph/text | shortbody/text">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
And the result is (see fiddle here):
<?xml version="1.0" encoding="UTF-8"?>
testing
shortbody
<result>
<p>
testing
shortbody
</p>
<paragraph>
A new version of xsltransform.net is released!
</paragraph>
<paragraph>
We have added the following new features:
</paragraph>
</result>
I cannot understand why templates for shortbody/text
are called twice so it ends outside my XML, but are not called twice for body/paragrap/text
? I have tried a lot of different ways to match, but every time shortbody
ends up outside my XML elements. Why is this and how can I change my XSLT so the template only matches the call from within <xsl:template match="body">
?
Upvotes: 0
Views: 730
Reputation: 116993
What you see is the result of the built-in template rules. Since you have no template matching /
or document
, the template applied to document
is this built-in template:
<xsl:template match="document">
<xsl:apply-templates/>
</xsl:template>
This applies templates to the children of document
, including shortbody
- and in the absence of a template matching shortbody
, the same the default template:
<xsl:template match="shortbody">
<xsl:apply-templates/>
</xsl:template>
applies templates to the children of shortbody
.
One way to prevent this is to organize your stylesheet this way:
<xsl:template match="/document">
<result>
<xsl:apply-templates/>
</result>
</xsl:template>
<xsl:template match="shortbody">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
<xsl:template match="paragraph">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
Note the absence of templates matching body
and text
. Here, the built-in template rules do exactly what is necessary.
Upvotes: 1
Reputation: 5432
You need to add another template that matches the root element, document
.
Try adding this:
<xsl:template match="document">
<xsl:apply-templates select="body"/>
</xsl:template>
The text outside your XML's root element is because default templates apply to the root element, which recursively get the child nodes processed, ending up copying the text nodes.
Upvotes: 2