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

Posted

技术标签:

【中文标题】为啥 index-of() 在应用于一系列唯一节点时会返回多个值?【英文标题】:Why does index-of() return multiple values when applied to a sequence of unique nodes?为什么 index-of() 在应用于一系列唯一节点时会返回多个值? 【发布时间】:2012-02-22 05:42:34 【问题描述】:

我正在使用 xpath2 的 index-of 值来返回排序后的节点序列中 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>

【问题讨论】:

【参考方案1】:

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

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

【讨论】:

谢谢,我想我会使用 index-of( for $n in $orderd return generate-id($n), generate-id(current()) ) 代替。 (Gerritt Imsieke 在 Saxon 的帮助列表中提出了这一建议。) @DavidR:你真的不需要使用generate-id(),可以编写你非常简单的index-of()函数,如我的回答所示。【参考方案2】:

不依赖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 运算符。

【讨论】:

对于有时格式化的代码没有缩进的讨厌的 SO 错误,我深表歉意。

以上是关于为啥 index-of() 在应用于一系列唯一节点时会返回多个值?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 lower_bound 应用于一系列未排序的向量元素?

为啥 sklearn 预处理 LabelEncoder inverse_transform 只适用于一列?

PostgreSQL:将表值函数串行应用于一组值和 UNION ALL 结果

VoiceOver 辅助功能专注于一系列元素

为啥我们不能在决策树中随机启动根节点?

为啥在导出的决策树中只​​标记父节点的边缘