XSLT: сортировка по нижнему из 2 значений
у меня есть XML, который отформатирован следующим образом:
<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>
мне нужно отсортировать продукты с помощью XSLT 1.0 (в восходящем или нисходящем порядке) на основе их текущей цены. Моя трудность заключается в том, что мне нужно сортировать по нижнему из двух возможных значений цены <orig>
и <offer>
если они оба существуют.
для приведенного выше примера правильный порядок будет такой:
- продукт 1 (самое низкое значение = 10)
- продукт 3 (самое низкое значение = 11)
- продукт 2 (наименьшее значение = 12)
любая помощь была бы очень признательна, так как я не могу найти аналогичный вопрос через поиск.
3 ответов
I. существует общее и чистое решение XSLT 1.0 - такое же простое, как это:
<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. Если price
других детей, в дополнение к offer
и 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=
"sum(price/orig[not(../offer <= .)])
+
sum(price/offer[not(../orig < .)])
"/>
</xsl:apply-templates>
</products>
</xsl:template>
</xsl:stylesheet>
III. Если мы это знаем ... --7--> не превышает 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. Проверка:
все три преобразования выше, при применении к предоставленному XML-документу:
<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>
произвести нужный, правильный результат:
<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>
решение II-единственное из трех, которое все еще дает правильный результат при применении на этот XML-документ (добавил minAcceptable
ребенок price
):
<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>
обратите внимание что ни один из других ответов не обрабатывает этот XML-документ правильно.
(ответ обновлен, чтобы включить мысли о XSLT 1.0 и 2.0)
I. XSLT 1.0:
обратите внимание, что XSLT 1.0 не имеет встроенного эквивалентно min()
; предполагая, что ваш парсер EXSLT
, вы можете использовать math:min()
функция для достижения решения, аналогичного приведенному ниже варианту XSLT 2.0.
II. XSLT 2.0:
здесь решение, которое использует функцию агрегации XPath 2.0 min()
.
когда это решение 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>
..применяется к предоставленному XML:
<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>
..желаемый результат получается:
<?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>
решение XSLT 1.0, которое не требует 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>