为什么 index-of() 在应用于唯一节点序列时会返回多个值?

发布于 2024-12-29 12:24:38 字数 1969 浏览 2 评论 0原文

我使用 xpath2 的索引值来返回已排序的节点序列中 current() 的索引。使用 SAXON,排序的节点序列是唯一的,但 index-of 返回两个值的序列。

这种情况不会一直发生,只是偶尔发生,但我找不到任何原因。有人可以解释一下发生了什么事吗?

我根据例程给出的这种奇怪行为的数据示例编写了一个最小的示例。

源数据是:

<data>
<student userID="1" userName="user1"/>
<session startedOn="01/16/2012 15:01:18">
</session>
<session startedOn="11/16/2011 13:31:33">
</session>
</data>

我的 xsl 文档将会话节点放入根模板最顶部的排序序列 $orderd 中:

<xsl:template match="/">
<xsl:variable name="nodes" as="node()*" select="/data/session"></xsl:variable>
<xsl:variable name="orderd" as="node()*">
<xsl:for-each select="$nodes">
<xsl:sort select="xs:dateTime(xs:dateTime(concat(substring(normalize-space(@startedOn),7,4),'-',substring(normalize-space(@startedOn),1,2),'-',substring(normalize-space(@startedOn),4,2),'T',substring(normalize-space(@startedOn),12,8)))
)" order="ascending"/>
    <xsl:sequence select="."/>
</xsl:for-each>
</xsl:variable>

由于节点已按 @startOn 排序,但顺序相反,因此序列 $orderd 应该相同作为文档排序序列 $nodes,除了相反的顺序。

当我使用 for-each 语句创建输出时,我发现在使用 index-of 测试时,两个节点在某种程度上被视为相同。

下面的代码用于输出数据(紧接在上面的块之后):

<output>
<xsl:for-each select="$nodes">
<xsl:sort select="position()" order="descending"></xsl:sort>
<xsl:variable name="index" select="index-of($orderd,current())" as="xs:integer*"></xsl:variable>
<xsl:variable name="pos" select="position()"></xsl:variable>        
<session reverse-documentOrder="{$pos}"  sortedOrder="{$index}"/>
</xsl:for-each>
</output>

正如输出(如下所示)所示,index-of 函数返回序列 (1,2),这意味着它将两个节点视为相同的。我已经检查了用于对值进行排序的表达式,它生成了独特且格式良好的日期时间字符串。

<output>
<session reverse=documentOrder="1"
        sortedOrder="1 2"/>
<session reverse-documentOrder="2"
        sortedOrder="1 2"/>
</output>

I'm using xpath2's index-of value to return the index of current() within a sorted sequence of nodes. Using SAXON, the sorted sequence of nodes are unique, yet index-of returns a sequence of two values.

This does not happen all the time, just very occasionally, but not for any reason I can find. Can someone please explain what is going on?

I have worked up a minimal example based on an example of data that routines gives this odd behavior.

The source data is:

<data>
<student userID="1" userName="user1"/>
<session startedOn="01/16/2012 15:01:18">
</session>
<session startedOn="11/16/2011 13:31:33">
</session>
</data>

My xsl document puts the session nodes into a sorted sequence $orderd at the very top of the root template:

<xsl:template match="/">
<xsl:variable name="nodes" as="node()*" select="/data/session"></xsl:variable>
<xsl:variable name="orderd" as="node()*">
<xsl:for-each select="$nodes">
<xsl:sort select="xs:dateTime(xs:dateTime(concat(substring(normalize-space(@startedOn),7,4),'-',substring(normalize-space(@startedOn),1,2),'-',substring(normalize-space(@startedOn),4,2),'T',substring(normalize-space(@startedOn),12,8)))
)" order="ascending"/>
    <xsl:sequence select="."/>
</xsl:for-each>
</xsl:variable>

Since the nodes were already ordered by @startOn but in the opposite order, the sequence $orderd should be the same as document-ordered sequence $nodes, except in reverse order.

When I create output using a for-each statement, I find that somehow the two nodes are seen as identical when tested using index-of.

The code below is used to output data (and comes immediately after the chunk above):

<output>
<xsl:for-each select="$nodes">
<xsl:sort select="position()" order="descending"></xsl:sort>
<xsl:variable name="index" select="index-of($orderd,current())" as="xs:integer*"></xsl:variable>
<xsl:variable name="pos" select="position()"></xsl:variable>        
<session reverse-documentOrder="{$pos}"  sortedOrder="{$index}"/>
</xsl:for-each>
</output>

As the output (shown below) indicates, the index-of function is returning the sequence (1,2), meaning that it sees both nodes as identical. I have checked the expression used to sort the values, and it produces distinct and well-formed date-Time strings.

<output>
<session reverse=documentOrder="1"
        sortedOrder="1 2"/>
<session reverse-documentOrder="2"
        sortedOrder="1 2"/>
</output>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

南城追梦 2025-01-05 12:24:38

不依赖于 generate-id() 函数(它是 XSLT 函数,而不是 XPath 函数),可以编写一个简单的 index-of() 函数对节点标识进行操作

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:my="my:my">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
    <xsl:variable name="vNum3" select="/*/*[3]"/>
    
    <xsl:variable name="vSeq" select="/*/*[1], /*/*[3], /*/*[3]"/>
    
 <xsl:template match="/">
   <xsl:sequence select="my:index-of($vSeq, $vNum3)"/>
 </xsl:template>
 
 <xsl:function name="my:index-of" as="xs:integer*">
  <xsl:param name="pSeq" as="node()*"/>
  <xsl:param name="pNode" as="node()"/>
  
  <xsl:for-each select="$pSeq">
    <xsl:if test=". is $pNode">
      <xsl:sequence select="position()"/>
    </xsl:if>
  </xsl:for-each>
 </xsl:function>
</xsl:stylesheet>

当此转换应用于以下 XML 文档时

<nums>
  <num>01</num>
  <num>02</num>
  <num>03</num>
  <num>04</num>
  <num>05</num>
  <num>06</num>
  <num>07</num>
  <num>08</num>
  <num>09</num>
  <num>10</num>
</nums>

返回所需的正确结果

2 3

解释 :使用 is 运算符。

Not relying on the generate-id() function, which is XSLT function, but not XPath function, one can write a simple index-of() function that operates on node identity:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:my="my:my">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
    <xsl:variable name="vNum3" select="/*/*[3]"/>
    
    <xsl:variable name="vSeq" select="/*/*[1], /*/*[3], /*/*[3]"/>
    
 <xsl:template match="/">
   <xsl:sequence select="my:index-of($vSeq, $vNum3)"/>
 </xsl:template>
 
 <xsl:function name="my:index-of" as="xs:integer*">
  <xsl:param name="pSeq" as="node()*"/>
  <xsl:param name="pNode" as="node()"/>
  
  <xsl:for-each select="$pSeq">
    <xsl:if test=". is $pNode">
      <xsl:sequence select="position()"/>
    </xsl:if>
  </xsl:for-each>
 </xsl:function>
</xsl:stylesheet>

when this transformation is applied on the following XML document:

<nums>
  <num>01</num>
  <num>02</num>
  <num>03</num>
  <num>04</num>
  <num>05</num>
  <num>06</num>
  <num>07</num>
  <num>08</num>
  <num>09</num>
  <num>10</num>
</nums>

the wanted, correct result is returned:

2 3

Explanation: Use of the is operator.

酷到爆炸 2025-01-05 12:24:38

文档 http://www.w3.org/TR/xpath- index-of 的functions/#func-index-of 表示“序列 $seqParam 中的项目根据 eq 运算符的规则与 $srchParam 进行比较。类型的值xs:untypedAtomic 的比较就像它们是 xs:string 类型一样。”。因此,您尝试比较非类型化元素节点,这意味着它们将作为字符串进行比较,并且两个 session 元素具有相同的空格(仅字符串内容)。这样,两者就被视为相等。

我不确定该建议什么,因为我不确定您想要实现什么,但我希望上述内容解释了您得到的结果。

The documentation http://www.w3.org/TR/xpath-functions/#func-index-of of index-of says "The items in the sequence $seqParam are compared with $srchParam under the rules for the eq operator. Values of type xs:untypedAtomic are compared as if they were of type xs:string.". So you are trying to compare untyped element nodes and that means they are compared as strings and both session elements have the same white space only string contents. That way both are compared as equal.

I am not sure what to suggest as I am not sure what you want to achieve but I hope the above explains the result you get.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文