Sorting 将前面的同级与xsl:sort一起使用

Sorting 将前面的同级与xsl:sort一起使用,sorting,xslt,Sorting,Xslt,我正在尝试使用前面的同级和后面的同级,并对记录子集进行排序。前面/下面的代码从原始xml顺序返回值的问题: <Salaries> <Salary> <Base>1000</Base> <CreatedDate xmlns:d7p1="http://schemas.datacontract.org/2004/07/System"> <d7p1:DateTime>2

我正在尝试使用前面的同级和后面的同级,并对记录子集进行排序。前面/下面的代码从原始xml顺序返回值的问题:

<Salaries>
    <Salary>
        <Base>1000</Base>
        <CreatedDate xmlns:d7p1="http://schemas.datacontract.org/2004/07/System">
            <d7p1:DateTime>2016-01-09T14:38:54.8440764Z</d7p1:DateTime>
            <d7p1:OffsetMinutes>0</d7p1:OffsetMinutes>
        </CreatedDate>
    </Salary>
    <Salary>
        <Base>2000</Base>
        <CreatedDate xmlns:d7p1="http://schemas.datacontract.org/2004/07/System">
            <d7p1:DateTime>2015-01-09T14:38:54.8440764Z</d7p1:DateTime>
            <d7p1:OffsetMinutes>0</d7p1:OffsetMinutes>
        </CreatedDate>
    </Salary>
    <Salary>
        <Base>3000</Base>
        <CreatedDate xmlns:d7p1="http://schemas.datacontract.org/2004/07/System">
            <d7p1:DateTime>2017-01-09T14:38:54.8440764Z</d7p1:DateTime>
            <d7p1:OffsetMinutes>0</d7p1:OffsetMinutes>
        </CreatedDate>
    </Salary>
</Salaries>

1000
2016-01-09T14:38:54.8440764Z
0
2000
2015-01-09T14:38:54.8440764Z
0
3000
2017-01-09T14:38:54.8440764Z
0
例如,当我使用a下的sort for each(salarys/Salary)和c#函数将偏移分钟数添加到日期中,并转换为一个长数字201701010000(以使xslt中的操作更容易)


排序工作正常,我按以下顺序取出记录:

  • 2000年
  • 一千
  • 三千
如果我使用前面的同级/前面的(和后面的)则会出现问题。我希望第一个记录(2000年)没有之前的记录,最后一个记录(3000年)没有后续记录。 但是,当我使用上一条/下一条时,我会从原始XML中获取上一条记录和下一条记录:

  • 2000年(前-1000/后-3000)
  • 1000(2000年之前/之后)
  • 3000(2000年之前/2000年之后)
我希望能够与前一条记录(按排序顺序)和当前记录(按排序顺序)进行比较:

  • 2000年(之前/之后-1000年)
  • 1000(2000年之前/3000年之后)
  • 3000(前1000/后1000)
我已经试过先于兄弟姐妹和先于兄弟姐妹

<xsl:value-of select="preceding::p:Salary[1]/p:Base"/>
<xsl:value-of select="preceding-sibling::p:Salary[1]/p:Base"/>
<xsl:value-of select="preceding::p:Salary[position()=1]/p:Base"/>

(薪资位于不同的命名空间(p) 这实际上是可能的,还是我必须使用变量来保存前一条记录的数据以进行比较


我正在使用xslt 1.0,前面的同级轴按文档顺序获取上下文节点的前面同级


要在排序后引用节点前面的同级,首先需要将排序后的节点存储在变量中,并且在XSLT 1.0中,将变量转换为节点集。

尽管XSLT/XPath经常提到“节点序列”,但实际上将其视为“节点引用序列”更为准确-因为,例如,同一个节点可以在序列中出现多次。对节点引用序列进行排序时,不会以任何方式更改单个节点,只会更改序列。这意味着节点仍然存在于其原始树中,与它们之前的位置完全相同,并且它们的父节点、同级节点和子节点都是完全相同的和以前一样,我很高兴

您需要的不是节点的前后兄弟节点,而是排序序列中位于它之前和之后的节点,这是一件完全不同的事情

实现这一点的一种方法是构造一个包含原始节点副本的新树,例如,如果您这样做,就会得到原始节点的副本

<xsl:variable name="x">
  <xsl:for-each ...>
    <xsl:sort ...>
      <xsl:copy-of select="."/>

然后,复制节点的同级关系将反映排序顺序。有一个小问题是,在XSLT 1.0中,$x是一个结果树片段,因此必须使用exslt:node-set()函数将其转换为节点集


事实上,在XSLT 1.0中,这可能是实现这一点的唯一方法,因为XSLT 1.0数据模型只有节点集,而没有序列,这意味着除了文档顺序之外,无法捕获和处理节点序列。2.0模型具有更大的灵活性和功能。如果可以的话,请升级—XSLT 1.0即将迎来20年rs old.

感谢Michael的回答。出于完整性考虑,发布在此处。由于xml中使用了空格,因此非常复杂:

  <!-- Puts the whole of the Salary Node into a variable-->
  <xsl:variable name="SALARY" >
    <xsl:copy-of select="p:Salaries" />
  </xsl:variable>

  <!-- Puts the the required key data into a node-set with the correct sort applied-->
  <xsl:variable name="SAL">
    <xsl:for-each select="msxsl:node-set($SALARY)//p:Salary">
      <xsl:sort select="number(cs:Convertdatetolong(cs:AddOffsetMinutes(substring(p:CreatedDate/d5p1:DateTime,1,19),p:CreatedDate/d5p1:OffsetMinutes)))" order="ascending"/>
      <xsl:copy-of select="." />
    </xsl:for-each>
  </xsl:variable>

<!-- Quick Output-->
  <xsl:for-each select="msxsl:node-set($SAL)//p:Salary">
    <xsl:text>Sa:</xsl:text>
    <xsl:value-of select="position()" />
    <xsl:text>Preceding:</xsl:text>
    <xsl:value-of select="preceding-sibling::p:Salary[1]/p:Base"/>
    <xsl:value-of select="$newline" />
    <xsl:text>Current:</xsl:text>
    <xsl:value-of select="p:Base"/>
    <xsl:value-of select="$newline" />
    <xsl:text>Following:</xsl:text>
    <xsl:value-of select="following-sibling::p:Salary[1]/p:Base"/>
    <xsl:value-of select="$newline"/>
  </xsl:for-each>

Sa:
先前的:
当前:
以下:

谢谢。我已经成功地将值和日期按我希望的顺序放入一个节点集中。我正在努力将它们循环成一个for each:似乎不起作用啊,我必须使用相同的名称空间。doh.msxsl:node set($SAL)//p:Salary
  <!-- Puts the whole of the Salary Node into a variable-->
  <xsl:variable name="SALARY" >
    <xsl:copy-of select="p:Salaries" />
  </xsl:variable>

  <!-- Puts the the required key data into a node-set with the correct sort applied-->
  <xsl:variable name="SAL">
    <xsl:for-each select="msxsl:node-set($SALARY)//p:Salary">
      <xsl:sort select="number(cs:Convertdatetolong(cs:AddOffsetMinutes(substring(p:CreatedDate/d5p1:DateTime,1,19),p:CreatedDate/d5p1:OffsetMinutes)))" order="ascending"/>
      <xsl:copy-of select="." />
    </xsl:for-each>
  </xsl:variable>

<!-- Quick Output-->
  <xsl:for-each select="msxsl:node-set($SAL)//p:Salary">
    <xsl:text>Sa:</xsl:text>
    <xsl:value-of select="position()" />
    <xsl:text>Preceding:</xsl:text>
    <xsl:value-of select="preceding-sibling::p:Salary[1]/p:Base"/>
    <xsl:value-of select="$newline" />
    <xsl:text>Current:</xsl:text>
    <xsl:value-of select="p:Base"/>
    <xsl:value-of select="$newline" />
    <xsl:text>Following:</xsl:text>
    <xsl:value-of select="following-sibling::p:Salary[1]/p:Base"/>
    <xsl:value-of select="$newline"/>
  </xsl:for-each>