XSLT:按2个值中的较低者排序

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> 如果它们都存在的话.

对于上面的示例,正​​确的顺序是:

  • 产品1(最低值= 10)
  • 产品3(最低值= 11)
  • 产品2(最低值= 12)

任何帮助将不胜感激,因为我似乎无法通过搜索找到类似的问题.

ABa*_*ach 6

(回答更新,包括对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)


Mad*_*sen 5

不需要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)


Dim*_*hev 5

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(../* &lt; .)]"/>
   </xsl:apply-templates>
  </products>
 </xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

II.如果price还有其他孩子offerorig - 在这种情况下一般的解决方案.上面(以及此问题的其他两个答案)无法正常工作.

以下是此案例的正确解决方案:

<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 &lt;= .)])
   +
     sum(price/offer[not(../orig &lt; .)])
    "/>
   </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文档.