XSLT适用于多种条件

时间:2018-08-25 05:10:14

标签: xslt

我对XSLT有疑问。基本上,我从下面的xml中寻找状态为“有货”的书籍的上次交易的唯一交付方式(邮件,家庭等)。例如,在下面的示例中,将显示“邮件”和“商店提货”。有人可以帮我XSLT吗?预先感谢。

<BookCollection>
    <Book>
        <status>In Stock</status>
        <name>The Hunt for Red October</name>
        <OrderHistory>
           <Order>
              <transactionid>sssss</transactionid>
               <date>2018-03-24</date>
               <Delivery>Home</Delivery>
           </Order>
           <Order>
              <transactionid>sssss</transactionid>
              <date>2018-04-23</date>
              <Delivery>Mail</Delivery>
           </Order>
        </Orderhistory>
    </Book>
    <Book>
        <name>A case of need</name>
        <status>Pending Stock</status>
        <OrderHistory>
           <Order>
              <transactionid>sssss</transactionid>
               <date>2018-08-24</date>
               <Delivery>Home</Delivery>
           </Order>
           <Order>
              <transactionid>sssss</transactionid>
              <date>2018-02-23</date>
              <Delivery>Store Pickup</Delivery>
           </Order>
        </Orderhistory>
    </Book>
    <Book>
        <name>Sharp Objects</name>
        <status>In Stock</status>
        <OrderHistory>
           <Order>
              <transactionid>sssss</transactionid>
               <date>2018-01-24</date>
               <Delivery>Home</Delivery>
           </Order>
           <Order>
              <transactionid>sssss</transactionid>
              <date>2018-05-23</date>
              <Delivery>Store Pickup</Delivery>
           </Order>
        </Orderhistory>
    </Book>
    <Book>
        <name>Happy Doomsday</name>
        <status>In Stock</status>
        <OrderHistory>
           <Order>
              <transactionid>aaa</transactionid>
               <date>2018-01-24</date>
               <Delivery>Mail</Delivery>
           </Order>
           <Order>
              <transactionid>bb</transactionid>
              <date>2018-03-23</date>
              <Delivery>Store Pickup</Delivery>
           </Order>
        </Orderhistory>
    </Book>
        .......
        .........
    </Book>
</BookCollection>

2 个答案:

答案 0 :(得分:0)

如果支持sort函数https://www.w3.org/TR/xpath-functions/#func-sort的高阶版本,则可以在纯XPath 3中实现:

distinct-values( 
    BookCollection/Book[status = 'In Stock'] 
    ! 
    sort(
      OrderHistory/Order, 
      (), 
      function($order) { xs:date($order/date) }
    )[last()]/Delivery
)

与Saxon 9.8 EE或PE或任何其他支持高阶功能的XSLT 3处理器一起使用的完整XSLT 3样式表将是

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

    <xsl:template match="/">
        <xsl:value-of 
            select="distinct-values( 
                        BookCollection/Book[status = 'In Stock'] ! sort(OrderHistory/Order, (), function($order) { xs:date($order/date) })[last()]/Delivery
                    )"
            separator=", "/>
    </xsl:template>

</xsl:stylesheet>

在不支持sort的高阶变体的XSLT 3处理器(如Saxon 9.8 HE)中,您需要将排序委托给自己的函数:

<xsl:template match="/">
    <xsl:value-of 
        select="distinct-values( 
                    BookCollection/Book[status = 'In Stock'] ! mf:sort(OrderHistory/Order)[last()]/Delivery
                )"
        separator=", "/>
</xsl:template>

<xsl:function name="mf:sort" as="element(Order)*">
    <xsl:param name="orders" as="element(Order)*"/>
    <xsl:perform-sort select="$orders">
        <xsl:sort select="xs:date(date)"/>
    </xsl:perform-sort>
</xsl:function>

示例https://xsltfiddle.liberty-development.net/bdxtqs

答案 1 :(得分:0)

通常,如果要查找不同的项目,可以将其视为一个分组问题,但是仅需要从每个组中输出一个值。在XSLT 1.0中,您将使用一种称为Muenchian Grouping的技术,这意味着定义这样的键:

<xsl:key name="orders" match="Order" use="Delivery" />

但是,您要按日期获取最后一个订单会增加复杂性。可能最简单的选择是为每个Order

创建一个仅包含最后OrderHistory个元素的变量
  <xsl:variable name="Orders">
    <xsl:for-each select="//Book[status = 'In Stock']/OrderHistory">
      <xsl:for-each select="Order">
        <xsl:sort select="date" order="descending" />
        <xsl:if test="position() = 1">
          <xsl:copy-of select="." />    
        </xsl:if>
      </xsl:for-each>
    </xsl:for-each>
  </xsl:variable>

但是,您需要使用扩展功能将此“结果树片段”转换为节点集,然后可以使用Muencjian分组技术。最像您的流程支持EXSLT,这意味着添加此命名空间

xmlns:exsl="http://exslt.org/common"

然后,要从变量中获取不同的值,请执行此操作...

<xsl:for-each select="exsl:node-set($Orders)/Order[generate-id() = generate-id(key('orders', Delivery)[1])]">

尝试使用此XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:exsl="http://exslt.org/common"
                version="1.0">

  <xsl:output method="text" />

  <xsl:key name="orders" match="Order" use="Delivery" />

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:variable name="Orders">
    <xsl:for-each select="//Book[status = 'In Stock']/OrderHistory">
      <xsl:for-each select="Order">
        <xsl:sort select="date" order="descending" />
        <xsl:if test="position() = 1">
          <xsl:copy-of select="." />    
        </xsl:if>
      </xsl:for-each>
    </xsl:for-each>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:for-each select="exsl:node-set($Orders)/Order[generate-id() = generate-id(key('orders', Delivery)[1])]">
      <xsl:if test="position() > 1">, </xsl:if>
      <xsl:value-of select="Delivery" />     
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

http://xsltfiddle.liberty-development.net/3NzcBtu/2(使用Microsoft的XSLTCompiledTransform,即XSLT 1.0)上查看此操作。

但是理想情况下,如果只是为了防止Martin Honnen感到厌烦,您应该考虑升级到XSLT 3.0。