XSLT:如何查找节点中唯一子节点的数量?

时间:2015-11-09 13:35:18

标签: xml xslt xslt-1.0

我的XML看起来像这样:

<foo>
    <bar name="a">
        <baz name="xyz">
            <time>2</time>
            <date>3</date>
        </baz>
    </bar>
    <bar name="b">
        <baz name="xyz">
            <time>2</time>
            <date>3</date>
        </baz>
    </bar>
    <bar name="c">
        <baz name="xyz">
            <time>2</time>
            <date>3</date>
        </baz>
    </bar>
</foo>

我正在编写一个需要像这样运行的XSL:如果所有baz个孩子都相同,那么doSomething其他doSomethingElse。我当前的节点是foo

我是XSLT的新手,我知道XSL中的条件。它现在看起来像这样:

<xsl:template match="foo">   
<xsl:choose>
    <xsl:when test="[My condition]"> 
        doSomething()
    </xsl:when>
    <xsl:otherwise>
        doSomethingElse()
    </xsl:otherwise>
</xsl:choose>
</xsl:template>

在当前示例中,它应doSomething(),因为所有baz元素都相同。

如果我找到唯一baz元素的数量,我可以测试它是否等于1。如果是,那么我将doSomething()其他doSomethingElse()

我该如何实现? MyCondition应该是什么?

PS:我的XSL版本是1.0

2 个答案:

答案 0 :(得分:2)

  

如果所有baz个孩子都相同,那么doSomething   doSomethingElse。我当前的节点是foo

这令人困惑,因为:

  • baz不是foo;
  • 的孩子
  • 你的标题是&#34; 找到独特孩子的数量&#34; - 但事实并非如此 必须找到它才能知道它们是否相同。

尝试类似:

<xsl:template match="foo">
    <xsl:variable name="first-baz" select="(bar/baz)[1]" />
    <xsl:choose>
        <xsl:when test="bar/baz[date!=$first-baz/date or time!=$first-baz/time]">
            <!-- THEY ARE NOT ALL SAME -->
        </xsl:when>
        <xsl:otherwise>
            <!-- THEY ARE ALL SAME -->
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

请注意,这假定每个baz都有datetime。否则,您需要测试not(date=$first-baz/date)等。

另见: http://www.jenitennison.com/xslt/grouping/muenchian.html

加了:

  

现在,假设所有bar/baz元素都具有相同的标签(不是   必须datetime,但要说abc),那会是什么   该案例的测试属性?

这使得它变得更加复杂。你仍然可以构建一个关键:

<xsl:key name="first-baz" match="foo/bar[1]/baz[1]/*" use="name()" />

然后进行测试:

<xsl:when test="bar/baz/*[. != key('first-baz', name())]">

如果存在baz的任何子项,其字符串值与同名的第一个baz的子节点不同,则返回true。

请注意,此处定义的密钥是文档范围的。如果要将测试限制为当前foo祖先,则必须在密钥中包含其ID。

答案 1 :(得分:0)

如果您想以通用方式执行此操作,那么您实际上正在将XSLT 1.0的功能扩展到其设计限制之外。但这是可以完成的。

编写一个名为deep-equal的命名模板,它将两个元素作为参数,并返回一个包含字符“F”的字符串,当且仅当它们不相等时(为了您的目的)。

可能看起来像这样:

<xsl:template name="deep-equal">
  <xsl:param name="a"/>
  <xsl:param name="b"/>
  <xsl:choose>
    <xsl:when test="local-name(a) != local-name(b)">F</xsl:when>
    <xsl:when test="namespace-uri(a) != namespace-uri(b)">F</xsl:when>
    <xsl:when test="normalize-space(a) != normalize-space(b)">F</xsl:when>
    <xsl:when test="count(a/*) != count(b/*)">F</xsl:when>
    <xsl:otherwise>
      <xsl:variable name="diffs">
        <xsl:for-each select="*">
          <xsl:variable name="position" select="position()"/>
          <xsl:call-template name="deep-equal">
            <xsl:with-param name="a" select="."/>
            <xsl:with-param name="b" select="$b/*[$position]"/>
          </xsl:call-template>
        </xsl:for-each>
      </xsl:variable>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

将此应用于所有相关元素对:

    <xsl:variable name="comparison">
      <xsl:for-each select="baz[position() &gt;= 2]">
       <xsl:call-template name="deep-equal">
         <xsl:with-param name="a" select="../baz[1]"/>
         <xsl:with-param name="b" select="."/>
       </xsl:call-template>
      </xsl:for-each>
    </xsl:variable>

测试结果是否包含“F”:

<xsl:if test="contains($comparison, 'F')">...</xsl:if>
相关问题