XSLT;在文档中查找最常见的元素值

时间:2009-06-02 11:24:45

标签: xml xslt

道歉,如果这是一个非常简单的问题;我不太习惯使用XSLT而且我在网上找不到太多建议,因为搜索结果中有很多污染!

我有一个以下形式的XML文档。它的主要目的是通过XSLT以几种方式重新格式化,以便以几种不同的格式显示。

<desk>
<drawer>
    <contents>pencils</contents>
    <quantity>2</quantity>
</drawer>
<drawer>
    <contents>pens</contents>
    <quantity>15</quantity>
</drawer>
<drawer>
    <contents>pencils</contents>
    <quantity>3</quantity>
</drawer>
<drawer>
    <contents>rulers</contents>
    <quantity>2</quantity>
</drawer>
</desk>

我想从xml中提取两条信息:i)平均数量; ii)xml中出现次数最常遇到的内容(即“铅笔”,因为它出现两次而不是“笔”,因为它具有最大的数量)。这个想法是,这可以通过管道传输到一个非常简单的shell脚本中。因此,我认为获取此信息的最简单方法是编写几个简短的xsl样式表,然后使用xsltproc获取数据。

第一条信息似乎很简单。样式表的核心就是这一行:

<xsl:value-of select="(sum(drawer/quantity)) div (count(drawer))" />

但是我被第二次卡住了。

我想我可以使用这样的东西循环浏览每个单独内容的列表:

<xsl:for-each select="drawer[not(contents = preceding-sibling::drawer/contents)]" />

但我不太确定如何计算具有$ current_contents的元素数量及其内容元素的值。我也无法看到按结果排序的简单方法,因此我可以获得最常遇到的内容值的名称。

我觉得在XSLT 2.0中使用各种group-by选项会更容易,但不幸的是,xsltproc似乎并不支持它。我们将非常感激地提供任何帮助。

非常感谢,

雅各

3 个答案:

答案 0 :(得分:2)

正如在XSLT中解决了很多问题一样,我认为你的答案是muenchian grouping。根据您感兴趣的任何数据进行分组,对于每个对象,可以使用xsl:sort,然后对第一个结果执行任何操作。

未经测试,头脑发热,可能更清洁的代码:

<xsl:key name="average" match="desk/drawer/contents" use="text()"/>

<xsl:template match="/">
    <xsl:for-each select="desk/drawer/contents[generate-id() = generate-id(key('average',text())[1])]">     
        <xsl:sort select="count(//desk/drawer/contents[text()=current()])"  order="descending"/>
        <xsl:if test="position()=1">
            Most common value: "<xsl:value-of select="current()"/>" (<xsl:value-of select="count(//desk/drawer/contents[text()=current()])"/>)
        </xsl:if>       
    </xsl:for-each>
</xsl:template>

答案 1 :(得分:0)

for-each中的排序是通过sort元素完成的。只需按数量排序(如果您只想要最频繁的话),添加<xsl:if test="position()=1">标记只能获得循环中的第一个。

<xsl:for-each select="drawer">
   <xsl:sort select="quantity" data-type="number" order="descending"/>
   <xsl:if test="position()=1">
      Most frequent: <xsl:value-of select="contents"> with <xsl:value-of select="quantity"> items
   </xsl:if>
</xsl:for-each>

答案 2 :(得分:0)

已经有一段时间了,但我认为这些方面的内容可能有用。

首先统计所有内容

<xsl:variable name="tally">
  <xsl:for-each select="drawer">
     <contents count="{count(drawer[contents = current()/contents])}"><xsl:value-of select="contents"/></contents>
  </xsl:for-each>
</xsl:variable>

请注意,每次都会计算重复的条目,$ tally将包含:

<contents count="2">pencils</contents>
<contents count="1">pens</contents>
<contents count="2">pencils</contents>
<contents count="1">rulers</contents>

然后使用它来查找没有其他具有更高计数的那个:

<xsl:variable name="mostfrequentcontents" select="$tally/contents[not($tally/contents/@count > @count)]" />

根据您的xslt处理器,您可能必须使用节点集函数将$ tally转换为节点集。