tutorial - xslt example
XSLT: ordena por el menor de 2 valores (3)
(respuesta actualizada para incluir pensamientos tanto en XSLT 1.0 como en 2.0)
I. XSLT 1.0:
Tenga en cuenta que XSLT 1.0 no tiene un equivalente incorporado a min()
; suponiendo que su analizador es compatible con EXSLT
, puede hacer uso de su función math:min()
para lograr una solución bastante similar a la variante XSLT 2.0 siguiente.
II. XSLT 2.0:
Aquí hay una solución que hace uso de la función de agregación XPath 2.0 min()
.
Cuando esta solución XSLT 2.0:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="products">
<products>
<xsl:apply-templates select="product">
<xsl:sort select="min(price/offer|price/orig)"
data-type="number" order="ascending" />
</xsl:apply-templates>
</products>
</xsl:template>
</xsl:stylesheet>
..se aplica al XML proporcionado:
<products>
<product>
<name>Product 1</name>
<price>
<orig>15</orig>
<offer>10</offer>
</price>
</product>
<product>
<name>Product 2</name>
<price>
<orig>13</orig>
<offer>12</offer>
</price>
</product>
<product>
<name>Product 3</name>
<price>
<orig>11</orig>
</price>
</product>
</products>
..el resultado deseado se produce:
<?xml version="1.0" encoding="UTF-8"?>
<products>
<product>
<name>Product 1</name>
<price>
<orig>15</orig>
<offer>10</offer>
</price>
</product>
<product>
<name>Product 3</name>
<price>
<orig>11</orig>
</price>
</product>
<product>
<name>Product 2</name>
<price>
<orig>13</orig>
<offer>12</offer>
</price>
</product>
</products>
Tengo un XML que está formateado de la siguiente manera:
<products>
<product>
<name>Product 1</name>
<price>
<orig>15</orig>
<offer>10</offer>
</price>
</product>
<product>
<name>Product 2</name>
<price>
<orig>13</orig>
<offer>12</offer>
</price>
</product>
<product>
<name>Product 3</name>
<price>
<orig>11</orig>
</price>
</product>
</products>
Necesito ordenar los productos usando XSLT 1.0 (en orden ascendente o descendente) según su precio actual. Mi dificultad radica en el hecho de que tengo que ordenar el menor de los dos posibles valores de precio <orig>
y <offer>
si ambos existen.
Para el ejemplo anterior, el orden correcto sería:
- Producto 1 (valor más bajo = 10)
- Producto 3 (valor más bajo = 11)
- Producto 2 (valor más bajo = 12)
Cualquier ayuda sería muy apreciada, ya que parece que no puedo encontrar una pregunta similar a través de la búsqueda.
I. Existe una solución XSLT 1.0 general y pura, tan simple como esto :
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<products>
<xsl:apply-templates select="*">
<xsl:sort data-type="number" select=
"price/*[not(../* < .)]"/>
</xsl:apply-templates>
</products>
</xsl:template>
</xsl:stylesheet>
II. Si el price
tiene otros hijos además de offer
y orig
- en este caso la solución general I. arriba (al igual que las otras dos respuestas a esta pregunta) no funciona correctamente.
Aquí hay una solución correcta para este caso :
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<products>
<xsl:apply-templates select="*">
<xsl:sort data-type="number" select=
"sum(price/orig[not(../offer <= .)])
+
sum(price/offer[not(../orig < .)])
"/>
</xsl:apply-templates>
</products>
</xsl:template>
</xsl:stylesheet>
III. Si sabemos que la offer
nunca excede orig
:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<products>
<xsl:apply-templates select="*">
<xsl:sort data-type="number"
select="price/offer | price/orig[not(../offer)]"/>
</xsl:apply-templates>
</products>
</xsl:template>
</xsl:stylesheet>
IV. Verificación :
Las tres transformaciones anteriores, cuando se aplican al documento XML proporcionado:
<products>
<product>
<name>Product 1</name>
<price>
<orig>15</orig>
<offer>10</offer>
</price>
</product>
<product>
<name>Product 2</name>
<price>
<orig>13</orig>
<offer>12</offer>
</price>
</product>
<product>
<name>Product 3</name>
<price>
<orig>11</orig>
</price>
</product>
</products>
producir el resultado deseado y correcto :
<products>
<product>
<name>Product 1</name>
<price>
<orig>15</orig>
<offer>10</offer>
</price>
</product>
<product>
<name>Product 3</name>
<price>
<orig>11</orig>
</price>
</product>
<product>
<name>Product 2</name>
<price>
<orig>13</orig>
<offer>12</offer>
</price>
</product>
</products>
La Solución II es la única de las tres que todavía produce el resultado correcto cuando se aplica en este documento XML (se agregó un price
minAcceptable
para el niño):
<products>
<product>
<name>Product 1</name>
<price>
<orig>15</orig>
<offer>10</offer>
<minAcceptable>8</minAcceptable>
</price>
</product>
<product>
<name>Product 2</name>
<price>
<orig>13</orig>
<offer>12</offer>
<minAcceptable>6</minAcceptable>
</price>
</product>
<product>
<name>Product 3</name>
<price>
<orig>11</orig>
<minAcceptable>7</minAcceptable>
</price>
</product>
</products>
Tenga en cuenta que ninguna de las otras respuestas procesa este documento XML correctamente.
Una solución XSLT 1.0 que no requiere EXSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="products">
<products>
<xsl:apply-templates select="product">
<xsl:sort select="(price/*[not(. > ../*)])[1]"
data-type="number" order="ascending" />
</xsl:apply-templates>
</products>
</xsl:template>
</xsl:stylesheet>