问题
If I run the following XSLT code:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kValueByVal" match="variable_name/@value"
use="."/>
<xsl:template match="assessment">
<xsl:for-each select="
/*/*/variable/attributes/variable_name/@value
[generate-id()
=
generate-id(key('kValueByVal', .)[1])
]
">
<xsl:value-of select=
"concat(., ' ', count(key('kValueByVal', .)), '
')"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
on the following XML:
<assessment>
<variables>
<variable>
<attributes>
<variable_name value="FRED"/>
</attributes>
</variable>
</variables>
<variables>
<variable>
<attributes>
<variable_name value="MORTIMER"/>
</attributes>
</variable>
</variables>
<variables>
<variable>
<attributes>
<variable_name value="FRED"/>
</attributes>
</variable>
</variables>
</assessment>
I get the desired output:
FRED 2
MORTIMER 1
(See my original question for more info, if you wish.)
However, if I run it on this input:
<ExamStore>
<assessment>
<variables>
<variable>
<attributes>
<variable_name value="FRED"/>
</attributes>
</variable>
</variables>
<variables>
<variable>
<attributes>
<variable_name value="MORTIMER"/>
</attributes>
</variable>
</variables>
<variables>
<variable>
<attributes>
<variable_name value="FRED"/>
</attributes>
</variable>
</variables>
</assessment>
</ExamStore>
I get nothing. (Note that I just wrapped the original input in an ExamStore tag.) I was expecting and hoping to get the same output.
Why don't I? How can I change the original XSLT code to get the same output?
回答1:
Well your select xpath /*/*/variable/attributes/variable_name/...
is no longer correct because you added another node higher in the node-tree.
If you want to have true independence you need to use something like:
//variable/attributes/variable_name/...
...(not the double slash at the start) but this is fairly dangerous because it will catch all occurences of that structure - be really sure that's what you mean.
Otherwise, just prepend your xpath with another /*
回答2:
When you introduced yet another level in the XML document, this screwed up the absolute XPath expression used in the original solution (taylored exactly after your original XML file).
Therefore, in order to make the XPath expression work in the new situation, just do the following:
Replace:
/*/*/variable/attributes/variable_name/@value
with
/*/*/*/variable/attributes/variable_name/@value
and now you again get the wanted neat result:
FRED 2
MORTIMER 1
I would never give you an "independent" solution, because you haven't provided any properties/guarantees/constraints about the set of possible XML documents on which you want to apply the transformation.
In your original question you used:
.//variables/variable/attributes/variable_name
off assessment
,
and this is why I used the absolute XPath expression in my solution. There was no guarantee that in another XML document some variable_name
elements wouldn't exist such that their chain of ancestors is not variables/variable/attributes
, If this were the case, this would mean that you probably were not interested in the values of such "irregular" variable_name
elements.
The lesson is that one should not be too specific in defining a question and then want general solutions. :)
回答3:
For real structure independence you should use:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kValueByVal" match="variable_name/@value"
use="."/>
<xsl:template match="variable_name[@value
[generate-id()
=
generate-id(key('kValueByVal', .)[1])
]]">
<xsl:value-of select=
"concat(@value, ' ', count(key('kValueByVal', @value)), '
')"/>
</xsl:template>
</xsl:stylesheet>
Result with first input:
FRED 2
MORTIMER 1
Result with second input:
FRED 2
MORTIMER 1
Note: Never use //
as fisrt XPath operator.
来源:https://stackoverflow.com/questions/3256791/counting-distinct-items-in-xslt-independent-of-depth