问题
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 thendoSomething
elsedoSomethingElse
. My current node isfoo
.
This is confusing because:
baz
are not children offoo
;- 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 necessarilydate
andtime
but saya
,b
andc
), 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() >= 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>
来源:https://stackoverflow.com/questions/33610324/xslt-how-to-find-the-count-of-unique-children-of-a-node