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

后端 未结 2 1854
臣服心动
臣服心动 2021-01-26 17:13

My XML looks like this:


    
        
            
            3&         


        
相关标签:
2条回答
  • 2021-01-26 17:59

    If you want to do this in a generic way then you are really stretching the capability of XSLT 1.0 beyond its design limits. But it can be done.

    Write a named template called deep-equal that takes two elements as its arguments and returns a string containing the character "F" if and only if they are not equal (for your purposes).

    It might look like this:

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

    Apply this to all relevant pairs of elements:

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

    Test if the result contains an "F":

    <xsl:if test="contains($comparison, 'F')">...</xsl:if>
    
    0 讨论(0)
  • 2021-01-26 18:07

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

    This is confusing because:

    • baz are not children of foo;
    • your title says "find the count of unique children" - but it is not necessary to find it in order to know if they are same.

    Try something like:

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

    Note that this assumes that every baz has a date and a time. Otherwise you need to test for not(date=$first-baz/date) etc.

    See also: http://www.jenitennison.com/xslt/grouping/muenchian.html


    Added:

    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?

    This makes it significantly more complex. Still you could construct a key:

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

    then make your test:

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

    This returns true if any child of baz exists whose string-value is different from an equally named node that is child of the first baz.

    Note that the key, as defined here, is document-wide. If you want to restrict the test to the current foo ancestor, then you must include its id in the key.

    0 讨论(0)
提交回复
热议问题