<xsl:template match="//foo[1]">
matches multiple elements (every <foo>
that has no previous <foo>
siblings)
<xsl:template match="(//foo)[1]">
is an error.
How do I match on just the first occurrence of an element in the document?
Here's an example of the input document:
<test>
<foo>1</foo>
<another>
<foo>2</foo>
</another>
<more>
<bar>3</bar>
<foo>4</foo>
</more>
<something>
<foo>5</foo>
<bar>6</bar>
<foo>7</foo>
</something>
<final>
<hum>8</hum>
<foo>9</foo>
<foo>10</foo>
</final>
</test>
My intention is to match the foo
with text 1
and no others . Assume foo
can occur anywhere in the document.
To match only the first occurrence of foo
in the document, you have to check both that there are no preceding foo
elements and that there are no foo
ancestors:
<xsl:template match="foo[not(preceding::foo or ancestor::foo)]">
Consider the following sample input XML:
<r>
<a/>
<foo>
<b/>
<foo/>
</foo>
<foo>
<foo/>
</foo>
<c/>
</r>
This XSLT,
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.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="foo[not(preceding::foo or ancestor::foo)]">
<FirstFoo>
<xsl:apply-templates/>
</FirstFoo>
</xsl:template>
<xsl:template match="foo">
<LaterFoo>
<xsl:apply-templates/>
</LaterFoo>
</xsl:template>
</xsl:stylesheet>
will output this XML,
<?xml version="1.0" encoding="UTF-8"?>
<r>
<a/>
<FirstFoo>
<b/>
<LaterFoo/>
</FirstFoo>
<LaterFoo>
<LaterFoo/>
</LaterFoo>
<c/>
</r>
but if you only check the preceding axis, you'll output this XML:
<?xml version="1.0" encoding="UTF-8"?>
<r>
<a/>
<FirstFoo>
<b/>
<FirstFoo/>
</FirstFoo>
<LaterFoo>
<LaterFoo/>
</LaterFoo>
<c/>
</r>
This problem can also be solved with a key where you then identify the first item in the group of foo
elements, using XSLT 2.0 this looks like
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:key name="foo-group" match="foo" use="node-name(.)"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="foo[. is key('foo-group', node-name(.))[1]]">
<foo id="first-foo">
<xsl:apply-templates/>
</foo>
</xsl:template>
</xsl:transform>
online at http://xsltransform.net/gWEamLb .
Using XSLT 1.0 you can use the local-name
or name
(or if needed namespace and name) to achieve the same, only as the is
operator does not exist you need to use generate-id
to identify the first item:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:key name="foo-group" match="foo" use="local-name()"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="foo[generate-id() = generate-id(key('foo-group', local-name())[1])]">
<foo id="first-foo">
<xsl:apply-templates/>
</foo>
</xsl:template>
</xsl:transform>
Online at http://xsltransform.net/ejivdH9 .
Finally using XSLT 3.0 we can write this as
<xsl:stylesheet
version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:key name="foo-group" match="foo" use="node-name(.)"/>
<xsl:param name="foo-name" as="xs:QName" select="xs:QName('foo')"/>
<xsl:template match="key('foo-group', $foo-name)[1]">
<foo id="first-foo">
<xsl:apply-templates/>
</foo>
</xsl:template>
</xsl:stylesheet>
It would be nicer not to need the <xsl:param name="foo-name" as="xs:QName" select="xs:QName('foo')"/>
and to use <xsl:template match="key('foo-group', xs:QName('foo'))[1]">
instead but even the more powerful XSLT 3.0 match patterns only allow us to use a function like key
if the arguments are literals or variables/parameters.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.