Mar*_*rkS 8 xslt xpath xslt-1.0
我有一些格式如下的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>
Run Code Online (Sandbox Code Playgroud)
我需要根据当前价格使用XSLT 1.0(按升序或降序)对产品进行排序.我的困难在于,我需要对两个可能的价格值中的较低者进行排序<orig>,<offer> 如果它们都存在的话.
对于上面的示例,正确的顺序是:
任何帮助将不胜感激,因为我似乎无法通过搜索找到类似的问题.
(回答更新,包括对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>
Run Code Online (Sandbox Code Playgroud)
..应用于提供的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>
Run Code Online (Sandbox Code Playgroud)
..想要的结果产生:
<?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>
Run Code Online (Sandbox Code Playgroud)
不需要EXSLT的XSLT 1.0解决方案:
<?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>
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
III.如果我们知道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="price/offer | price/orig[not(../offer)]"/>
</xsl:apply-templates>
</products>
</xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
产生想要的,正确的结果:
<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>
Run Code Online (Sandbox Code Playgroud)
当应用于此XML文档(添加minAcceptable子项price)时,解决方案II是三者中唯一仍然产生正确结果的解决方案:
<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>
Run Code Online (Sandbox Code Playgroud)
请注意,没有其他答案正确处理此XML文档.