XSLT:如何匹配给定元素的第一次出现?

时间:2016-10-29 17:53:01

标签: xml xslt

<xsl:template match="//foo[1]">

匹配多个元素(每个<foo>没有以前的<foo>兄弟姐妹)

<xsl:template match="(//foo)[1]">

是一个错误。

如何匹配文档中第一次出现的元素?

以下是输入文档的示例:

<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>

我的目的是将foo与文字1没有其他相匹配。假设foo可以在文档中任何地方出现。

2 个答案:

答案 0 :(得分:5)

要仅匹配文档中第一次出现的foo,您必须检查前面没有foo个元素,并且没有foo个祖先:

<xsl:template match="foo[not(preceding::foo or ancestor::foo)]">

请考虑以下示例输入XML:

<r>
  <a/>
  <foo>
    <b/>
    <foo/>
  </foo>
  <foo>
    <foo/>
  </foo>
  <c/>
</r>

此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>

将输出此XML,

<?xml version="1.0" encoding="UTF-8"?>
<r>
  <a/>
  <FirstFoo>
      <b/>
      <LaterFoo/>
  </FirstFoo>
  <LaterFoo>
      <LaterFoo/>
  </LaterFoo>
  <c/>
</r>

但如果您只检查前一个轴,则输出此XML:

<?xml version="1.0" encoding="UTF-8"?>
<r>
  <a/>
  <FirstFoo>
      <b/>
      <FirstFoo/>
  </FirstFoo>
  <LaterFoo>
      <LaterFoo/>
  </LaterFoo>
  <c/>
</r>

答案 1 :(得分:0)

这个问题也可以通过一个键来解决,然后你可以使用XSLT 2.0识别foo元素组中的第一个项目,这看起来像

<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>

在线http://xsltransform.net/gWEamLb

使用XSLT 1.0,您可以使用local-namename(或者如果需要的命名空间和名称)来实现相同的效果,只有在is运算符不存在时才需要使用generate-id标识第一项:

<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>

http://xsltransform.net/ejivdH9在线。

最后使用XSLT 3.0,我们可以将其写为

<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>

不需要<xsl:param name="foo-name" as="xs:QName" select="xs:QName('foo')"/>并使用<xsl:template match="key('foo-group', xs:QName('foo'))[1]">会更好,但即使是更强大的XSLT 3.0匹配模式也只允许我们使用像key这样的函数是文字或变量/参数。