问题
I have the following XML:
<assessment>
<section>
<item>
<attributes>
<variables>
<variable>
<variable_name value="MORTIMER"/>
</variable>
</variables>
</attributes>
</item>
<item>
<attributes>
<variables>
<variable>
<variable_name value="FRED"/>
</variable>
</variables>
</attributes>
</item>
<item>
<attributes>
<variables>
<variable>
<variable_name value="MORTIMER"/>
</variable>
</variables>
</attributes>
</item>
</section>
</assessment>
I have the following XSLT to process that XML:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kValueByVal" match="item//variables//variable_name"
use="@value"/>
<xsl:template match="assessment">
<xsl:for-each select="
.//item//variables//variable_name/@value
">
<xsl:value-of select=
"concat(., ' ', count(key('kValueByVal', .)), '
')"/>
<br/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
It outputs the following, which is almost what I want:
MORTIMER 2
FRED 1
MORTIMER 2
It lists each of the variable_names and how many times each occurs. The only problem is that it gives this count once for each time the variable_name occurs instead of only once.
This is what I want it to output:
MORTIMER 2
FRED 1
How do I modify the XSLT code to give me that? Note that we're using XSLT 1.0.
The following solution, which seems like it should work, outputs nothing:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kValueByVal" match="item//variables//variable_name"
use="@value"/>
<xsl:template match="assessment">
<xsl:for-each select=".//item//variables//variable_name/@value[generate-id()
=
generate-id(key('kValueByVal',.)[1])]">
<xsl:value-of select=
"concat(., ' ', count(key('kValueByVal', .)), '
')"/>
<br/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
回答1:
You really need to understand how the Muenchian grouping works, otherwise you'd be asking variations of the same questions forever.
Do read Jeni Tennison's tutorial.
Here is a solution for your latest question:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kVarNameByVal" match="variable_name"
use="@value"/>
<xsl:template match=
"variable_name[generate-id()
=
generate-id(key('kVarNameByVal', @value)[1])
]
">
<xsl:value-of select=
"concat(@value, ' ', count(key('kVarNameByVal', @value)), '
')"/>
<br/>
</xsl:template>
</xsl:stylesheet>
When this transformation is performed on the provided XML document, the wanted result is produced:
MORTIMER 2
FRED 1
回答2:
Seeing as you're using XSLT 2.0, I'd use the built-in grouping features. (For earlier versions, you'd probably want to look into Muenchian grouping.)
<?xml version="1.0" ?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kValueByVal" match="item//variables//variable_name"
use="@value"/>
<xsl:template match="assessment">
<xsl:for-each-group select=".//item//variables//variable_name" group-by="@value">
<xsl:value-of select="concat(current-grouping-key(), ' ', count(current-group()), '
')"/>
<br/>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
回答3:
This will do it in XSLT 1.0.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kValueByVal" match="item//variables//variable_name"
use="@value"/>
<xsl:template match="assessment">
<xsl:for-each select="
//item//variables//variable_name[not(@value=ancestor::item/preceding-sibling::item//variables//variable_name/@value)]
">
<xsl:value-of select=
"concat(@value, ' ', count(key('kValueByVal', @value)), '
')"/>
<br/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The output I get is
MORTIMER 2
<br />FRED 1
<br />
Note that it assumes a bit more about the document structure (the ancestor::item bit), but you should be able to take it from there.
回答4:
You get what you are asking for:
<xsl:for-each select=".//item//variables//variable_name/@value">
Wich means: for each one of these attributes
When grouping, you must say: for each one of these one of a kind
And, how do you know wich are one of a kind? With Muenchian method:
<xsl:for-each select=".//item//variables//variable_name/@value[generate-id()
=
generate-id(key('kValueByVal',.)[1])]">
That means: the ones been the first with that key.
EDIT: Also, avoid //
when you know input schema.
EDIT: Now I can see that you change the key... So, for you new key, who is first of a kind? Yes! variable_name
element:
<xsl:for-each select=".//item//variables//variable_name[generate-id()
=
generate-id(key('kValueByVal',@value)[1])]">
回答5:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kValueByVal" match="item//variables//variable_name"
use="@value"/>
<xsl:template match="assessment">
<xsl:for-each
select="//item//variables//variable_name[
generate-id() =
generate-id(key('kValueByVal', @value)[1])]">
<xsl:value-of select=
"concat(./@value, ' ', count(key('kValueByVal', ./@value)), '
')"/>
<br/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
来源:https://stackoverflow.com/questions/3266407/counting-distinct-items-in-xslt-and-listing-only-once