XSLT:如何找到节点的唯一 children 计数?

XSLT: How to find the count of unique children of a node?

我的 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 children 都相同,则 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 元素的数量,我可以测试它是否等于一。如果是,那我就doSomething() else doSomethingElse()

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

PS: 我的 XSL 版本是 1.0

If all the baz children are same then doSomething else doSomethingElse. My current node is foo.

这令人困惑,因为:

  • baz不是foo的child仁;
  • 您的标题说“查找唯一 children 的计数”- 但事实并非如此 必须找到它才能知道它们是否相同。

试试这样的东西:

<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 都有一个 date 和一个 time。否则你需要测试 not(date=$first-baz/date)

另请参阅: http://www.jenitennison.com/xslt/grouping/muenchian.html


已添加:

Now, assuming all the bar/baz elements have the same tags (not necessarily date and time but say a, b and c), what would be the test attribute for that case?

这使得它变得更加复杂。你仍然可以构造一个密钥:

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

然后进行测试:

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

如果 baz 中的任何 child 存在其 string-value 不同于第一个 child 的同名节点,则此 returns 为真=13=].

请注意,此处定义的密钥是 document-wide。如果你想将测试限制在当前的 foo 祖先,那么你必须在键中包含它的 id。

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

编写一个名为 deep-equal 的命名模板,它将两个元素作为其参数,returns 一个包含字符 "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>