2012-04-27 29 views
6

Supongamos que tengo variables $a, $b, $c y $d que contienen números. Me gustaría obtener el valor más pequeño (más grande). Mi enfoque típico XSLT 1.0 a esto es¿Cómo seleccionar el valor más pequeño de un grupo de variables?

<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> 

Sin embargo, mi procesador XSLT 1.0 se queja con

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

¿Cómo puedo calcular el mínimo (máximo) de los valores dados?


Por supuesto, podría usar una larga serie de declaraciones <xsl:when> y comprobar todas las combinaciones, pero me gustaría más bien como una solución más pequeña.

+0

El principal problema, pienso, es que los números no son nodos. – cHao

+0

Creo que el problema principal es que en XSLT 1.0, la expresión en 'select' debe evaluar a un conjunto de nodos. (http://www.w3.org/TR/xslt#for-each) –

Respuesta

5

Si las variables tienen valores (no de forma dinámica computarizada) estáticamente definido, entonces algo como lo siguiente se puede hacer con 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> 

Cuando se aplica esta transformación en cualquier documento XML (no usado), el, resultado correcto querido se produce:

Smallest: 1 
    Largest: 9 

II. Ahora, supongamos que las variables están definidas dinámicamente.

que podemos hacer algo como esto (pero necesitará la función xxx:node-set() extensión):

<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> 

cuando se aplica esta transformación en el siguiente documento 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> 

la deseado, se produce el resultado correcto:

Smallest: 1 
    Largest: 9 
+0

¿Entonces el documento ('') apunta al xml XSLT en sí? ¿Tendrá que volver a analizarlo? Otro truco de fantasía que aprendí, gracias. +1 –

+0

@PavelVeller: Sí, esto se usa a menudo si desea tener datos en línea en la hoja de estilos XSLT y no en un archivo separado. Sí, el nuevo análisis se realiza en el momento de la ejecución y no es demasiado eficiente. También le puede interesar ver la segunda parte de la solución. –

+0

La segunda parte hace el truco, gracias. Mis variables son en sí mismas mínimas de expresiones más complicadas sobre el árbol de entrada, por lo que ciertamente no son estáticas. – bitmask

1

nunca he tenido que hacer esto en 1,0 (utilizo 2.0), pero se puede hacer esto:

<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> 

Tiene que haber una mejor manera sin embargo.

+0

Mi pensamiento es exactamente. Como dije, ya tengo esto como un plan de contingencia.Pero no es agradable, y ciertamente no escala. – bitmask

+0

@bitmask - Eché de menos el último párrafo de tu pregunta donde dijiste que podrías hacer esto. Esperemos que alguien (@DimitreNovatchev probablemente) tenga una solución mucho mejor. –

3

Esta solución XSLT 1.0 utiliza plantillas recursivas para analizar una lista delimitada de valores para devolver el valor mínimo/máximo de la lista.

<?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> 

Cuando se ejecuta, produce el siguiente resultado:

Smallest: 1 
Largest: 9 
Cuestiones relacionadas