可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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() >= 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>