对XPath进行排序的结果与多个选择参数的顺序相同

时间:2010-12-09 18:31:18

标签: ruby xml xslt xpath nokogiri

我有一个XML文档如下:

<objects>
  <object uid="0" />
  <object uid="1" />
  <object uid="2" />
</objects>

我可以使用以下查询选择多个元素:

doc.xpath("//object[@uid=2 or @uid=0 or @uid=1]")

但是这会按照它们在XML文档中声明的顺序返回元素(uid = 0,uid = 1,uid = 2),并且我希望结果与执行XPath查询的顺序相同(uid = 2,uid = 0,uid = 1)。

我不确定单独使用XPath是否可行,并且已经研究过XSLT排序,但我没有找到解释如何实现此目的的示例。

我在Ruby中使用Nokogiri库。

5 个答案:

答案 0 :(得分:4)

XPath 1.0无法指定所选节点的顺序

XPath 2.0允许sequence个具有任何特定订单的节点

//object[@uid=2], //object[@uid=1]

评估一个序列,其中object@uid=2所有object个项目都在@uid=1

之前

如果没有可用的XPath 2.0引擎,仍然可以使用XSLT以便以任何所需的顺序输出节点。

在此特定情况下以下XSLT指令的序列:

<xsl:copy-of select="//object[@uid=2]"/>

<xsl:copy-of select="//object[@uid=1]"/>

产生所需的输出

<object uid="2" /><object uid="1" />

答案 1 :(得分:1)

我假设您使用的是XPath 1.0。 W3C规范说: XPath中的主要语法结构是表达式。表达式与生产Expr匹配。计算表达式以生成一个对象,该对象具有以下四种基本类型之一:

* node-set (an unordered collection of nodes without duplicates)
* boolean (true or false)
* number (a floating-point number)
* string (a sequence of UCS characters)

所以我认为你不能简单地使用XPath重新订购。 (规范的其余部分定义了文档顺序和反向文档顺序,因此如果后者执行您想要的操作,您可以使用适当的轴(例如在前面)获取它。

在XSLT中,您可以使用属性的name()来使用<xsl:sort>XSLT FAQ非常好,你应该在那里找到答案。

答案 2 :(得分:0)

我认为在xpath中没有办法实现它,但是如果你想切换到XSLT,你可以使用xsl:sort标签:

<xsl:for-each select="//object[@uid=1 or @uid=2]">
  <xsl:sort: select="@uid" data-type="number" />
  {insert new logic here}
</xsl:for-each>

这里有更完整的信息: http://www.w3schools.com/xsl/el_sort.asp

答案 3 :(得分:0)

XSLT示例:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:param name="pSequence" select="'2 1'"/>
    <xsl:template match="objects">
        <xsl:for-each select="object[contains(concat(' ',$pSequence,' '),
                                              concat(' ',@uid,' '))]">
            <xsl:sort select="substring-before(concat(' ',$pSequence,' '),
                                               concat(' ',@uid,' '))"/>
            <xsl:copy-of select="."/>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

输出:

<object uid="2" /><object uid="1" />

答案 4 :(得分:0)

这就是我在Nokogiri的表现:

require 'nokogiri'

xml = '<objects><object uid="0" /><object uid="1" /><object uid="2" /></objects>'

doc = Nokogiri::XML(xml)
objects_by_uid = doc.search('//object[@uid="2" or @uid="1"]').sort_by { |n| n['uid'].to_i }.reverse
puts objects_by_uid

运行输出:

<object uid="2"/>
<object uid="1"/>

搜索的替代方法是:

objects_by_uid = doc.search('//object[@uid="2" or @uid="1"]').sort { |a,b| b['uid'].to_i <=> a['uid'].to_i }

如果您不喜欢将sort_byreverse一起使用。

XPath对于定位和检索节点很有用,但是我们想要做的过滤通常在访问器中过于复杂,所以我让语言来做,无论是Ruby,Perl还是Python。我把过滤逻辑放在哪里是基于XML数据集的大小以及是否有很多不同的uid值我想要抓取。有时候让XPath引擎完成繁重工作是有道理的,有时候让XPath更容易抓住所有object节点并使用调用语言进行过滤。