转换和排序不同的日期格式

Transform and sort different date formats

我的任务是使用不同的日期格式转换生成的 XML 文件 作为排序标准。作为 XSLT 的新手,我想知道是否可以使用单个转换来统一日期格式和排序。

来源 XML 的示例在这里:

<?xml version="1.0"?>
<summary>
    <incoming>
        <delivery incDate="2013-11-08"/>
    </incoming>
    <outgoing>
        <delivery outDate="20131108"/>
    </outgoing>
    <repairs>
        <repair repairDate="2013-11-08 11:25:34"/>
    </repairs>
</summary>

这就是我想要实现的目标:

<?xml version="1.0"?>
<summary>
    <actions>
        <action type="incoming" dateTime="2013-11-08 00:00:00"/>
        <action type="repair"   dateTime="2013-11-08 11:25:34"/>
        <action type="outgoing" dateTime="2013-11-08 23:59:59"/>
    </actions>
</summary>

我做了什么?

  1. 统一所有日期格式。
  2. <incoming> 的所有孩子上附加时间 00:00:00。
  3. <outgoing> 的所有孩子上附加时间 23:59:59。
  4. 添加名称为 parent 的属性。
  5. 按日期排序输出。

在 XSLT 2.0 中,这非常简单,因为您可以使用两遍方法,首先生成所需的输出,然后对它们进行排序:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

  <xsl:output method="xml" indent="yes" />

  <xsl:template match="/">
    <summary>
      <actions>
        <xsl:perform-sort>
          <xsl:sort select="@dateTime" />
          <xsl:apply-templates select="summary/*/*" />
        </xsl:perform-sort>
      </actions>
    </summary>
  </xsl:template>

  <xsl:template match="incoming/delivery">
    <action type="incoming" dateTime="{@incDate} 00:00:00"/>
  </xsl:template>

  <xsl:template match="outgoing/delivery">
    <action type="outgoing" dateTime="{substring(@outDate, 1, 4)}-{substring(@outDate, 5, 2)}-{substring(@outDate, 7, 2)} 23:59:59"/>
  </xsl:template>

  <xsl:template match="repairs/repair">
    <action type="repair" dateTime="{@repairDate}"/>
  </xsl:template>
</xsl:stylesheet>

在这里,我们使用 apply-templates 然后 为每个输入元素生成输出,然后使用 XPath 表达式 perform-sort 对这些生成的元素进行排序( @dateTime) 相对于生成的 XML 而不是原始的

如果您限于 1.0,则这不是一个选项,因为您只能根据输入 XML 中的内容进行排序,而不是生成的输出。所以我们需要提出一个 XPath 1.0 表达式,它可以处理三种日期格式中的任何一种,并生成合适的排序键

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

  <xsl:output method="xml" indent="yes" />

  <xsl:template match="/">
    <summary>
      <actions>
        <xsl:apply-templates select="summary/*/*">
          <xsl:sort select="
             translate(
               concat(
                 @incDate, @outDate, @repairDate,
                 substring('000000', 6*not(@incDate) + 1),
                 substring('235959', 6*not(@outDate) + 1)
               ),
               '-: ',
               ''
             )" />
        </xsl:apply-templates>
      </actions>
    </summary>
  </xsl:template>

  <!-- the other three templates are unchanged -->

这使用了很多技巧,最著名的是

substring('000000', 6*not(@incDate) + 1)

这取决于很多事情:

  • not() 的参数被视为布尔值。
  • 当您将节点集视为布尔值时,空集为假,非空集为真
  • 将布尔值转换为数字给出 1 表示真,0 表示假

因此,如果目标节点具有 incDate 属性,则效果是 return 字符串 000000,如果没有,则为空字符串。最后的 concat 构建一个字符串,对于 incDate 看起来像 YYYY-MM-DD000000,对于 outDate 看起来像 YYYYMMDD235959,对于 repairDatetranslate 去除所有空格、连字符和冒号,将这三者带入可以按字典顺序进行比较的通用格式。