【问题标题】:Sorting XPath results in the same order as multiple select parameters以与多个选择参数相同的顺序对 XPath 结果进行排序
【发布时间】:2010-12-09 18:31:18
【问题描述】:

我有一个如下的 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 排序,但我还没有找到一个示例来解释我如何实现这一点。

我正在使用 Nokogiri 库使用 Ruby。

【问题讨论】:

  • 好问题,+1。请参阅我的答案以获得解释和两种不同的解决方案:XPath 2.0 和 XSLT 1.0 解决方案。

标签: ruby xml xslt xpath nokogiri


【解决方案1】:

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

XPath 2.0 允许sequence 具有任何特定顺序的节点

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

计算为一个序列,其中所有带有@uid=2object 项目在所有带有@uid=1object 项目之前

如果没有可用的 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" />

【讨论】:

  • 感谢 Dimitre,我已将此标记为有用。我已选择接受 Alejandro 的答案,因为这正是我所需要的 - 包括传递包含我正在搜索的 ID 的参数的能力。
  • @Cameron:你没有在问题中提到你想要参数化。当然,如果需要参数化,那么排序就是要走的路。大概一个月前,我用一个使用参数和排序的解决方案回答了一个类似的问题——这根本不是什么新鲜事。 :)
【解决方案2】:

我假设您使用的是 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() 来使用 &lt;xsl:sort&gt;XSLT FAQ 很好,你应该在那里找到答案。

【讨论】:

    【解决方案3】:

    一个 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】:

      我认为在 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

      【讨论】:

        【解决方案5】:

        这就是我在 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 节点并在调用语言中过滤更容易。

        【讨论】:

        • 感谢格雷格的回复。不幸的是,我不需要按 UID 排序的结果,我需要指定它们返回的确切顺序。我在问题中使用 1,2 和 2,1 的示例可能令人困惑 - 为了清楚起见,我已经更新了。
        • @Cameron Yule,是的,这令人困惑。
        猜你喜欢
        • 1970-01-01
        • 2014-11-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-27
        • 2023-03-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多