2012-04-27 7 views
6

Предположим, у меня есть переменные $a, $b, $c и $d которые все имеют номера. Я хотел бы получить наименьшее (наибольшее) значение. Мой типичный XSLT 1.0 подход к этому являетсяКак выбрать наименьшее значение из группы переменных?

<xsl:variable name="minimum"> 
    <xsl:for-each select="$a | $b | $c | $d"> 
    <xsl:sort 
     select="." 
     data-type="number" 
     order="ascending" /> 
    <xsl:if test="position()=1"><xsl:value-of select="." /></xsl:if> 
    </xsl:for-each> 
</xsl:variable> 

Однако мой XSLT 1,0 процессор жалуется

runtime error: file stylesheet.xslt line 106 element for-each
The 'select' expression does not evaluate to a node set.

Как я могу вычислить минимум (максимум) заданных значений?


Конечно, я мог бы использовать длинную серию <xsl:when> заявлений и проверить все комбинации, но я бы предпочел, как уменьшенная решение.

+0

Основная проблема, я думаю, является то, что числа не являются узлами. – cHao

+0

Я думаю, что основная проблема заключается в том, что в XSLT 1.0 выражение в 'select' должно оцениваться в наборе узлов. (http://www.w3.org/TR/xslt#for-each) –

ответ

5

Если переменные имеют статически определенные значения (не динамически вычисляемое), то что-то вроде следующего может быть сделано с помощью XSLT 1.0:

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

<xsl:variable name="vA" select="3"/> 
<xsl:variable name="vB" select="1"/> 
<xsl:variable name="vC" select="9"/> 
<xsl:variable name="vD" select="5"/> 

<xsl:template match="/"> 
    <xsl:for-each select= 
     "document('')/*/xsl:variable 
     [contains('|vA|vB|vC|vD|', concat('|', @name, '|'))] 
      /@select 
     "> 
     <xsl:sort data-type="number" order="ascending"/> 

     <xsl:if test="position() = 1"> 
     Smallest: <xsl:value-of select="."/> 
     </xsl:if> 
     <xsl:if test="position() = last()"> 
     Largest: <xsl:value-of select="."/> 
     </xsl:if> 
    </xsl:for-each> 
</xsl:template> 
</xsl:stylesheet> 

Когда это преобразование применяется на любой документ XML (не б), то хотел, правильный результат получается:

Smallest: 1 
    Largest: 9 

II. Теперь предположим, что переменные динамически определены.

Мы можем сделать что-то вроде этого (но нужна функция xxx:node-set() расширения):

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:ext="http://exslt.org/common"> 
<xsl:output method="text"/> 

<xsl:variable name="vA" select="number(/*/*[3])"/> 
<xsl:variable name="vB" select="number(/*/*[1])"/> 
<xsl:variable name="vC" select="number(/*/*[9])"/> 
<xsl:variable name="vD" select="number(/*/*[5])"/> 

<xsl:template match="/"> 
    <xsl:variable name="vrtfStore"> 
     <num><xsl:value-of select="$vA"/></num> 
     <num><xsl:value-of select="$vB"/></num> 
     <num><xsl:value-of select="$vC"/></num> 
     <num><xsl:value-of select="$vD"/></num> 
    </xsl:variable> 

    <xsl:for-each select="ext:node-set($vrtfStore)/*"> 
     <xsl:sort data-type="number" order="ascending"/> 

     <xsl:if test="position() = 1"> 
     Smallest: <xsl:value-of select="."/> 
     </xsl:if> 
     <xsl:if test="position() = last()"> 
     Largest: <xsl:value-of select="."/> 
     </xsl:if> 
    </xsl:for-each> 
</xsl:template> 
</xsl:stylesheet> 

когда это преобразование применяется на следующий документ XML:

<nums> 
    <num>01</num> 
    <num>02</num> 
    <num>03</num> 
    <num>04</num> 
    <num>05</num> 
    <num>06</num> 
    <num>07</num> 
    <num>08</num> 
    <num>09</num> 
    <num>10</num> 
</nums> 

в требуется, правильный результат:

Smallest: 1 
    Largest: 9 
+0

Значит, документ ('') указывает на XSLT xml? Должно ли это пересмотреть его? Еще один причудливый трюк, который я учу, спасибо. +1 –

+0

@PavelVeller: Да, это часто используется, если вы хотите иметь встроенные данные в таблице стилей XSLT, а не в отдельном файле. Да, повторный анализ выполняется во время выполнения, и это не слишком эффективно. Возможно, вам также будет интересна вторая часть решения. –

+0

Вторая часть делает трюк, спасибо. Мои переменные сами по себе являются минимальными более сложными выражениями над входным деревом, поэтому, конечно, не статичны. – bitmask

1

Я никогда не приходилось делать это в 1.0 (я использую 2.0), но вы можете сделать это:

<xsl:variable name="minimum"> 
    <xsl:choose> 
     <xsl:when test="$b > $a and $c > $a and $d > $a"><xsl:value-of select="$a"/></xsl:when> 
     <xsl:when test="$a > $b and $c > $b and $d > $b"><xsl:value-of select="$b"/></xsl:when> 
     <xsl:when test="$b > $c and $a > $c and $d > $c"><xsl:value-of select="$c"/></xsl:when> 
     <xsl:when test="$b > $d and $c > $d and $a > $d"><xsl:value-of select="$d"/></xsl:when> 
    </xsl:choose> 
    </xsl:variable> 

Там должен быть лучше, хотя.

+0

Мое мышление точно. Как я уже сказал, у меня уже есть это как план на случай непредвиденных обстоятельств.Но это не приятно, и это, конечно, не масштабируется. – bitmask

+0

@bitmask - Я полностью пропустил последний абзац вашего вопроса, где вы сказали, что можете это сделать. Надеюсь, у кого-то (возможно, @DimitreNovatchev) будет гораздо лучшее решение. –

3

В этом решении XSLT 1.0 используются рекурсивные шаблоны для разбора ограниченного списка значений для возврата значения min/max из списка.

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

    <xsl:variable name="a" select="'3'"/> 
    <xsl:variable name="b" select="'1'"/> 
    <xsl:variable name="c" select="'9'"/> 
    <xsl:variable name="d" select="'5'"/> 

    <xsl:template match="/"> 
     <xsl:text>&#xa;Smallest: </xsl:text> 
     <xsl:call-template name="min"> 
      <xsl:with-param name="values" select="concat($a,',',$b,',',$c,',',$d)"/>  
     </xsl:call-template> 

     <xsl:text>&#xa;Largest: </xsl:text> 
     <xsl:call-template name="max"> 
      <xsl:with-param name="values" select="concat($a,',',$b,',',$c,',',$d)"/>  
     </xsl:call-template> 
    </xsl:template> 

    <xsl:template name="min"> 
     <xsl:param name="values" /> 
     <xsl:param name="delimiter" select="','"/> 
     <xsl:param name="min"/> 

     <xsl:variable name="currentValue" > 
      <xsl:choose> 
       <xsl:when test="contains($values, $delimiter)"> 
        <xsl:value-of select="substring-before($values,$delimiter)"/> 
       </xsl:when> 
       <xsl:otherwise> 
        <xsl:value-of select="$values"/> 
       </xsl:otherwise> 
      </xsl:choose> 
     </xsl:variable> 

     <xsl:variable name="minimumValue"> 
      <xsl:choose> 
       <xsl:when test="$min and $min > $currentValue"> 
        <xsl:value-of select="$currentValue"/> 
       </xsl:when> 
       <xsl:when test="$min"> 
        <xsl:value-of select="$min"/> 
       </xsl:when> 
       <xsl:otherwise> 
        <xsl:value-of select="$currentValue" /> 
       </xsl:otherwise> 
      </xsl:choose> 
     </xsl:variable> 

     <xsl:choose> 
      <xsl:when test="substring-after($values,$delimiter)"> 
       <xsl:call-template name="min"> 
        <xsl:with-param name="min" select="$minimumValue" /> 
        <xsl:with-param name="values" select="substring-after($values,$delimiter)" /> 
        <xsl:with-param name="delimiter" select="$delimiter"/> 
       </xsl:call-template> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:value-of select="$minimumValue" /> 
      </xsl:otherwise> 
     </xsl:choose>     
    </xsl:template> 


    <xsl:template name="max"> 
     <xsl:param name="values" /> 
     <xsl:param name="delimiter" select="','"/> 
     <xsl:param name="max"/> 

     <xsl:variable name="currentValue" > 
      <xsl:choose> 
       <xsl:when test="contains($values, $delimiter)"> 
        <xsl:value-of select="substring-before($values,$delimiter)"/> 
       </xsl:when> 
       <xsl:otherwise> 
        <xsl:value-of select="$values"/> 
       </xsl:otherwise> 
      </xsl:choose> 
     </xsl:variable> 

     <xsl:variable name="maximumValue"> 
      <xsl:choose> 
       <xsl:when test="$max and $currentValue > $max"> 
        <xsl:value-of select="$currentValue"/> 
       </xsl:when> 
       <xsl:when test="$max"> 
        <xsl:value-of select="$max"/> 
       </xsl:when> 
       <xsl:otherwise> 
        <xsl:value-of select="$currentValue" /> 
       </xsl:otherwise> 
      </xsl:choose> 
     </xsl:variable> 

     <xsl:choose> 
      <xsl:when test="substring-after($values,$delimiter)"> 
       <xsl:call-template name="max"> 
        <xsl:with-param name="max" select="$maximumValue" /> 
        <xsl:with-param name="values" select="substring-after($values,$delimiter)" /> 
        <xsl:with-param name="delimiter" select="$delimiter"/> 
       </xsl:call-template> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:value-of select="$maximumValue" /> 
      </xsl:otherwise> 
     </xsl:choose>     
    </xsl:template> 
</xsl:stylesheet> 

При выполнении производит следующий вывод:

Smallest: 1 
Largest: 9 
Смежные вопросы