Sorting XPath results in the same order as multiple select parameters

这一生的挚爱 提交于 2019-12-11 05:38:13

问题


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.


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



回答2:


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



回答3:


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.




回答4:


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




回答5:


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.



来源:https://stackoverflow.com/questions/4401774/sorting-xpath-results-in-the-same-order-as-multiple-select-parameters

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!