XSLT:按下的2个值(XSLT: Sort by the lower of 2 values)

2019-09-22 04:39发布

我有一些的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)

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

Answer 1:

一,有一种普遍的和纯粹的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>

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>

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>

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>

解决方案二是只有当这个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文档。



Answer 2:

(回答更新,包括在两个XSLT 1.0和2.0的思想)

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

..the想要的结果产生:

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


Answer 3:

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


文章来源: XSLT: Sort by the lower of 2 values