问题
I'm using xslt version 2, I'm trying to transform an xml to a fo output, and I'm stuck on a specific problematic.
Here is what looks like my input:
<a1/>
<a1/>
<b/>
<c/>
<d/>
<a2/>
<b/>
<c/>
<a1/>
<a1/>
<a1/>
<a1/>
<b/>
<c/>
<d/>
This data, functionally speaking, contains a list of 'sets' defined by a1|a2,b?,c?,d?.
My problem is that I don't see how I can count the number of a1 tags for a specific 'set'.
Indeed, I have written my xsl and I get an output like that:
<fo:table>
<fo:row>
<fo:cell>b: </fo:cell>
<fo:cell>b value</fo:cell>
</fo:row>
<fo:row>
<fo:cell>a1: </fo:cell>
<fo:cell>number of a1 ???</fo:cell> <-- what I am trying to retrieve
</fo:row>
<fo:row>
...
</fo:row>
...
</fo:table>
I have done an apply-template on a1+|a2 tags, and I do nothing if a1 tag has a following sibling that equals to a1. I think there must be a way to count the tags with preceding sibling (but then how to insure to count only the corresponding one?)
Any hints would be appreciated!
Edit: On the above example of input, the first count should be 2:
<a1/>
<a1/>
<b/>
<c/>
<d/>
then it should be 4, and not 6:
<a1/>
<a1/>
<a1/>
<a1/>
<b/>
<c/>
<d/>
回答1:
Your question is not really clear.
What should "the corresponding one" be?
Counting all a1
before the current one would be:
count(preceding-sibling::a1)
if needed you may add predicates like:
count(preceding-sibling::a1[/corresponding one/])
To count only the presiding sibling a1 which are in a sequence of a1 nodes try this: Find the first node which is not an a1.
<xsl:variable name="firstnota1" select="preceding-sibling::*[not (self::a1)][1]" />
The wonted result than, is count all nodes before the current a1 minus the count of nodes before the first not a1 + this node it self.
<xsl:value-of select="count(preceding-sibling::*)
- count($firstnota1/preceding-sibling::* | $firstnota1)"/>
Or without variable:
<xsl:value-of
select="count(preceding-sibling::*)
- count( preceding-sibling::*[not (self::a1)][1]
/preceding-sibling::*
| preceding-sibling::*[not (self::a1)][1] )"/>
回答2:
I would use for-each-group group-adjacent="boolean(self::a1)">
e.g.
<xsl:stylesheet
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="root">
<xsl:for-each-group select="*" group-adjacent="boolean(self::a1)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<xsl:value-of select="'Count is: ', count(current-group())"/>
</xsl:when>
<xsl:otherwise>
<!-- process other elements here -->
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
If the input is
<root>
<a1/>
<a1/>
<b/>
<c/>
<d/>
<a2/>
<b/>
<c/>
<a1/>
<a1/>
<a1/>
<a1/>
<b/>
<c/>
<d/>
</root>
then Saxon 9 outputs Count is: 2Count is: 4
so it outputs the numbers you want (badly formatted, admittedly). If you don't get any output then perhaps the elements you have posted have a different parent element than the one I have chosen (i.e. root
). Or you use namespaces and the self::a1
needs to be adapted.
回答3:
Try following xpath
count(preceding-sibling::a1)-count(preceding-sibling::b[1]/preceding-sibling::a1)
It means: count of preceding a1 at all minus count of a1 preceding previous b
来源:https://stackoverflow.com/questions/17630574/how-to-count-the-number-of-same-immediate-preceding-siblings-in-xsl