Сортировка результатов XPath в том же порядке, что и параметры множественного выбора - PullRequest
1 голос
/ 09 декабря 2010

У меня есть 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, но я не нашел пример, который объясняет, как я мог бы достичь этого.

Я работаю вРубин с библиотекой Нокогири.

Ответы [ 5 ]

4 голосов
/ 09 декабря 2010

В XPath 1.0 нет способа указать порядок выбранных узлов .

XPath 2.0 допускает последовательность узлов с любым конкретным порядком :

//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 голос
/ 09 декабря 2010

Я предполагаю, что вы используете 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 вы можете использовать <xsl:sort>, используя имя () атрибута. XSLT FAQ очень хорош, и вы должны найти ответ там.

0 голосов
/ 09 декабря 2010

Вот как я бы это сделал в Нокогири:

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_by с reverse.

XPath полезен для поиска и извлечения узлов, но часто фильтрация, которую мы хотим сделать, становится слишком сложной в аксессоре, поэтому я позволяю языку делать это, будь то Ruby, Perl или Python. То, где я размещаю логику фильтрации, основано на том, насколько велик набор данных XML и есть ли много различных значений uid, которые я хочу получить. Иногда имеет смысл разрешить движку XPath выполнять тяжелую работу, в других случаях проще позволить XPath захватить все узлы object и выполнить фильтрацию на языке вызова.

0 голосов
/ 09 декабря 2010

Пример 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" />
0 голосов
/ 09 декабря 2010

Не думаю, что есть способ сделать это в 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

...