Selecting the lowest value from and xml without so

2019-06-24 14:10发布

问题:

I'm transforming an xml document to an html table using xslt, and now what I want to do is to change the background color of the cell containing the lowest "price" value without sorting my list.

I'm new on this, so what I'm doing is following the W3C schools examples. The xml file looks like this:

    <?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<catalog>
<cd>
    <title>Empire Burlesque</title>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
</cd>
<cd>
    <title>Black angel</title>
    <artist>Savage Rose</artist>
    <country>EU</country>
    <company>Mega</company>
    <price>11.90</price>
    <year>1995</year>
</cd>
<cd>
    <title>For the good times</title>
    <artist>Kenny Rogers</artist>
    <country>UK</country>
    <company>Mucik Master</company>
    <price>8.70</price>
    <year>1995</year>
</cd>
</catalog>

And what I want to obtain is something similar to this but without ordering the elements in the list by price. So I want to maintain the original order in the XML document.

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <!-- Edited by XMLSpy® -->
    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
     <html>
      <body>
        <h2>My CD Collection</h2>
        <table border="1">
          <tr bgcolor="#9acd32">
          <th>Title</th>
          <th>Artist</th>
          <th>Price</th>
        </tr>
        <xsl:for-each select="catalog/cd">
        <xsl:sort select="price" order="ascending" data-type="number"/>
          <tr>
            <td><xsl:value-of select="title"/></td>
            <td><xsl:value-of select="artist"/></td>
            <xsl:choose>
              <xsl:when test="(position() = 1)">
                <td bgcolor="#ff00ff">
                <xsl:value-of select="price"/></td>
              </xsl:when>
            <xsl:otherwise>
                <td><xsl:value-of select="price"/></td>
            </xsl:otherwise>
            </xsl:choose>
          </tr>
        </xsl:for-each>
        </table>
      </body>
      </html>
    </xsl:template>
    </xsl:stylesheet>

Thanks in advance for your help.


EDIT: I think I have found a solution for my question:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
  <html>
  <body>
    <h2>My CD Collection</h2>
    <table border="1">
      <tr bgcolor="#9acd32">
        <th>Title</th>
        <th>Artist</th>
        <th>Price</th>
      </tr>
      <xsl:for-each select="catalog/cd">
       <tr>
        <td><xsl:value-of select="title"/></td>
        <td><xsl:value-of select="artist"/></td>
        <xsl:choose>
          <xsl:when test="price=/catalog/cd/price[not(. &gt; ../../cd/price)][1]">
            <td bgcolor="#ff00ff">
            <xsl:value-of select="price"/></td>
          </xsl:when>
        <xsl:otherwise>
            <td><xsl:value-of select="price"/></td>
        </xsl:otherwise>
        </xsl:choose>
      </tr>

      </xsl:for-each>
    </table>
  </body>
  </html>
</xsl:template>

回答1:

You seem to have answered your question already, but I will throw in a couple of other things in here.

(1) You can avoid re-calculating the minimum price at each iteration through the <cd/> elements by adding a variable to the stylesheet or the template to do the calculation once:

<xsl:variable name="least" select="math:min(/catalog/cd/price)"/>

(2) That "$least" variable declaration uses the EXSLT math:min function (namespace is xmlns:math="http://exslt.org/math"), which may be available to your XSLT 1.0 processor. If you have a 2.0 processor, you can use the built-in "min()".

If nothing else, a min() function is more readable for the next person.

(3) This is purely stylistic, but you can reduce the branching a bit by using xsl:attribute.

<td>
    <xsl:if test="price=$lowest">
        <xsl:attribute name="bgcolor">#ff00ff</xsl:attribute>
    </xsl:if>
    <xsl:value-of select="price"/>
</td>


回答2:

And what I want to obtain is something similar to this but without ordering the elements in the list by price.

The fact that sorting is used for finding the minimum doesn't mean that any elements will be re-ordered. Finding the minimum using <xsl:sort> is in practice the fastest way to do so in XSLT 1.0 -- and doesn't require any extension functions:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:variable name="vLowest">
     <xsl:for-each select="/*/*/price">
      <xsl:sort data-type="number"/>
      <xsl:if test="position()=1"><xsl:value-of select="."/></xsl:if>
     </xsl:for-each>
    </xsl:variable>

 <xsl:template match="/*">
          <html>
          <body>
            <h2>My CD Collection</h2>
            <table border="1">
              <tr bgcolor="#9acd32">
                <th>Title</th>
                <th>Artist</th>
                <th>Price</th>
              </tr>
              <xsl:apply-templates select="cd"/>
            </table>
          </body>
          </html>
 </xsl:template>

 <xsl:template match="cd">
        <tr>
          <td><xsl:value-of select="title"/></td>
          <td><xsl:value-of select="artist"/></td>
          <td>
            <xsl:if test="price=number($vLowest)">
              <xsl:attribute name="bgcolor">#ff00ff</xsl:attribute>
            </xsl:if>
            <xsl:value-of select="price"/>
          </td>
        </tr>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document:

<catalog>
    <cd>
        <title>Empire Burlesque</title>
        <artist>Bob Dylan</artist>
        <country>USA</country>
        <company>Columbia</company>
        <price>10.90</price>
        <year>1985</year>
    </cd>
    <cd>
        <title>Black angel</title>
        <artist>Savage Rose</artist>
        <country>EU</country>
        <company>Mega</company>
        <price>11.90</price>
        <year>1995</year>
    </cd>
    <cd>
        <title>For the good times</title>
        <artist>Kenny Rogers</artist>
        <country>UK</country>
        <company>Mucik Master</company>
        <price>8.70</price>
        <year>1995</year>
    </cd>
</catalog>

the wanted, correct result is produced:

<html>
   <body>
      <h2>My CD Collection</h2>
      <table border="1">
         <tr bgcolor="#9acd32">
            <th>Title</th>
            <th>Artist</th>
            <th>Price</th>
         </tr>
         <tr>
            <td>Empire Burlesque</td>
            <td>Bob Dylan</td>
            <td>10.90</td>
         </tr>
         <tr>
            <td>Black angel</td>
            <td>Savage Rose</td>
            <td>11.90</td>
         </tr>
         <tr>
            <td>For the good times</td>
            <td>Kenny Rogers</td>
            <td bgcolor="#ff00ff">8.70</td>
         </tr>
      </table>
   </body>
</html>