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

发布于 2024-10-07 01:17:48 字数 472 浏览 4 评论 0原文

我有一个 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 进行工作。

I have an XML document as follows:

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

I can select multiple elements using the following query:

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

But this returns the elements in the same order they're declared in the XML document (uid=0, uid=1, uid=2) and I want the results in the same order as I perform the XPath query (uid=2, uid=0, uid=1).

I'm unsure if this is possible with XPath alone, and have looked into XSLT sorting, but I haven't found an example that explains how I could achieve this.

I'm working in Ruby with the Nokogiri library.

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

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

发布评论

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

评论(5

走过海棠暮 2024-10-14 01:17:48

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

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

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

计算结果为一个序列,其中所有带有 @uid=2object 项目位于所有带有 object 项目之前code>@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" />

There is no way in XPath 1.0 to specify the order of the selected nodes.

XPath 2.0 allows a sequence of nodes with any specific order:

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

evaluates to a sequence in which all object items with @uid=2 precede all object items with @uid=1

If one doesn't have anXPath 2.0 engine available, it is still possible to use XSLT in order to output nodes in any desired order.

In this specific case the sequence of the following XSLT instructions:

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

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

produces the desired output:

<object uid="2" /><object uid="1" />
半山落雨半山空 2024-10-14 01:17:48

我假设您使用的是 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 中,您可以使用 < /code> 使用属性的 name()。XSLT 常见问题解答非常好,你应该在那里找到答案。

I am assuming you are using XPath 1.0. The W3C spec says:
The primary syntactic construct in XPath is the expression. An expression matches the production Expr. An expression is evaluated to yield an object, which has one of the following four basic types:

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

So I don't think you can re-order simply using XPath. (The rest of the spec defines document order and reverse document order, so if the latter does what you want you can get it using the appropriate axis (e.g. preceding).

In XSLT you can use <xsl:sort> using the name() of the attribute. The XSLT FAQ is very good and you should find an answer there.

星軌x 2024-10-14 01:17:48

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" />

An XSLT example:

<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>

Output:

<object uid="2" /><object uid="1" />
独守阴晴ぅ圆缺 2024-10-14 01:17:48

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

I don't think there is a way to do it in xpath but if you wish to switch to XSLT you can use the xsl:sort tag:

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

more complete info here:
http://www.w3schools.com/xsl/el_sort.asp

自找没趣 2024-10-14 01:17:48

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

This is how I'd do it in 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

Running that outputs:

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

An alternative to the search would be:

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

if you don't like using sort_by with the reverse.

XPath is useful for locating and retrieving the nodes but often the filtering we want to do gets too convoluted in the accessor so I let the language do it, whether it's Ruby, Perl or Python. Where I put the filtering logic is based on how big the XML data set is and whether there are a lot of different uid values I'll want to grab. Sometimes letting the XPath engine do the heavy lifting makes sense, other times its easier to let XPath grab all the object nodes and filter in the calling language.

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