每组的XSLT max

时间:2019-03-06 10:19:05

标签: xml xslt

我在xml中有数据,该数据提供了产品及其价格的列表。寻找XSLT可以帮助我得出每种产品的最高价格。尝试可能还没有运气。请帮忙。

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <Product>
    <name>P1</name>
    <price>10</price>
  </Product>
  <Product>
    <name>P1</name>
    <price>20</price>
  </Product>
  <Product>
    <name>P2</name>
    <price>5</price>
  </Product>
  <Product>
    <name>P3</name>
    <price>8</price>
  </Product>
  <Product>
    <name>P1</name>
    <price>30</price>
  </Product>
</root>

预期输出

Product	Price
P1	30
P2	5
P3	8

1 个答案:

答案 0 :(得分:0)

Using XSLT 2.0 it is quite easy:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:output method="text"/>

  <xsl:template match="root">
    <xsl:text>Product Price&#x0A;</xsl:text>
    <xsl:for-each-group select="Product" group-by="name">
      <xsl:value-of select="concat(current-grouping-key(),
        '      ', max(current-group()/price), '&#x0A;')"/>
    </xsl:for-each-group>
  </xsl:template>
</xsl:transform>

As you can see, there is needed a for-each-group loop, selecting Product elements and grouping them on the value of child name element.

Inside the loop, current-grouping-key() function provides the key of the current group (value of name element) and max(current-group()/price) computes the maximal price.

For a working example see http://xsltransform.net/pNEhB3c

Edit following your comment concerning XSLT version

Your task can be accomplished also in XSLT 1.0, using so called Muenchian Grouping (for more detailed description search the Web).

The first thing to do is to create a key, grouping elements to be grouped (Product) under some grouping key (in our case name):

<xsl:key name="prods" match="Product" use="name"/>

Then the actual grouping can be performed in a for-each loop, using a predicate with a Muenchian Grouping idiom inside:

[generate-id()=generate-id(key('prods', name)[1])]

An additional point of difficulty is that XSLT 1.0 has so much limited capabilities, that it even does not have max function.

To circumvent this limitation, we have to write a template (I called it maximum), which takes a sequence of elements, sorts them (descending) and outputs the first (maximum) value.

This template is called for price children elements of all Product elements from the current group.

So the whole script can look like below:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="text"/>
  <xsl:key name="prods" match="Product" use="name"/>

  <xsl:template match="root">
    <xsl:text>Product Price&#x0A;</xsl:text>
    <xsl:for-each select="Product[generate-id()=
      generate-id(key('prods', name)[1])]">
      <xsl:value-of select="concat(name, '      ')"/>
      <xsl:call-template name="maximum">
        <xsl:with-param name="src" select="key('prods', name)/price"/>
      </xsl:call-template>
      <xsl:value-of select="'&#x0A;'"/>
    </xsl:for-each>
  </xsl:template>

 <xsl:template name="maximum">
    <xsl:param name="src"/>
    <xsl:for-each select="$src">
      <xsl:sort select="." data-type="number" order="descending"/>
      <xsl:if test="position()=1">
        <xsl:value-of select="."/>
      </xsl:if>
    </xsl:for-each>
  </xsl:template>
</xsl:transform>

For a working example see http://xsltransform.net/pNEhB3c/1