Counting distinct items in XSLT independent of depth

假装没事ソ 提交于 2019-12-24 10:37:57

问题


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', .)), '&#xA;')"/>
  </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)), '&#xA;')"/>
 </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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!