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

匿名 (未验证) 提交于 2019-12-03 02:38:01

问题:

My XML looks like this:

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

I am writing an XSL that needs to function like this: If all the baz children are same then doSomething else doSomethingElse. My current node is foo.

I am new to XSLT and I am aware of the conditionals in XSL. It looks something like this as of now:

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

In the current example, it should doSomething() as all the baz elements are the same.

If I find out the number of unique baz elements, I can test whether it is equal to one. If it is, then I will doSomething() else doSomethingElse()

How should I implement this? What should MyCondition be?

PS: My XSL version is 1.0

回答1:

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.



回答2:

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> 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!