Главная » XSLT » Фильтрация узлов

0

Задача

Требуется отобрать узлы в зависимости от хранящихся в них данных, возмож­но, в сочетании с ограничениями на имена или позицию.

Решение

Во многих мини-рецептах в разделе 1.1 для фильтрации узлов использовались пре­дикаты, но они налагали условия только на позицию или имя узла. Ниже мы рассмот­рим предикаты, фильтрующие по содержимому. Во всех примерах из этого раздела пе­ред каждым предикатом стоит имя X, но вместо него можно было бы задать произвольное путевое выражение, в частности, любое из рассмотренных в разделе 1.1.

В примерах ниже употребляются операторы сравнения из XPath 2.0 (eq, ne, ® lt, le, gt и ge) вместо привычных операторов =, !=, <, <=, >, >=. Объясня­ется это тем, что для сравнения атомарных значений новые операторы предпочтительнее. В XPath 1.0 есть только старые операторы, так что вам придется внести коррективы. Новые операторы были введены в XPath 2.0, так как обладают более простой семантикой и потому могут оказаться эф­фективнее. Недостатки старых операторов проявляются, когда по обе сто­роны сравнения оказываются последовательности. В рецепте 1.8 эта тема рассматривается более подробно.

Для тех, кто работает с XPath 2.0, будет уместно сделать еще одно замечание. В этой версии используется информация о типах, если доступна схема. В некоторых из приведенных ниже выражений это может стать причиной ошибок типизации. Например, X[@a = 10] и X[@a = ’10’] – не одно и то же, если атрибут a имеет целочисленный тип. Мы предполагаем, что схема не задана, и потому все атомар­ные значения имеют тип untypedAtomic. Дополнительную информацию по это­му поводу см. в рецептах 1.9 и 1.10.

(: Отобрать детей элемента X, у которых есть атрибут a. :) X[@a]

(: Отобрать детей элемента X, у которых есть хотя бы один атрибут. :) X[@*]

(: Отобрать детей элемента X, у которых есть хотя бы три атрибута. :) X[count(@*) > 2]

(: Отобрать детей элемента X, для которых сумма всех атрибутов меньше 7. :) X[sum(foreach $a in @* return number($a)) < 7] (: В XSLT 1.0 надо писать sum(@*) &lt; 7 :)

(: Отобрать детей элемента X, у которых нет атрибутов. :) X[not(@*)]

(: Отобрать детей элемента X, у которых есть атрибут a со значением ’10’. :) X[@a eq ’10’]

(: Отобрать детей элемента X, у которых есть дочерний элемент Z со значением ’10’. :) X[Z eq ’10’]

(: Отобрать детей элемента X, у которых есть дочерний элемент Z со значением, отличным от ’10’. :) X[Z ne ’10’]

(: Отобрать детей элемента X, у которых есть хотя бы один текстовый дочерний элемент. :) X[text()]

(: Отобрать детей элемента X, у которых есть текстовый дочерний элемент, содержащий хотя бы один непробельный символ. :) X[text()][normalize-space(.)]

(: Отобрать детей элемента X, у которых есть хотя бы один дочерний элемент. :) X[node()]

(: Отобрать тех детей элемента X, которые содержат комментарий. :) X[comment()]

(: Отобрать детей элемента X, у которых атрибут @a имеет числовое значение, меньшее 10. Это выражение одинаково хорошо работает в XPath 1.0 и XPath 2.0 независимо от того, имеет ли атрибут @a числовой или строковый тип. :) X[number(@a) < 10]

(: Отобрать элемент X, если у него есть хотя бы один предшествующий брат

с именем Z, который содержит атрибут у, не равный 10. :) X[preceding-sibling::Z/@y ne ’10’]

(: Отобрать детей элемента X, строковое значение которых состоит из единственного пробела. :) X[. = ‘ ‘]

(: Странный способ получить пустую последовательность! :) X[false()]

(: То же, что X. :) X[true()]

(: Элементы X, имеющие ровно 5 дочерних элементов. :) X[count(*) eq 5]

(: Элементы X, имеющие ровно 5 дочерних узлов (включая элементы, текст, комментарии и узлы с командами обработки, но исключая узлы-атрибуты). :) X[count(node()) eq 5]

(: Элементы X, имеющие ровно 5 дочерних узлов любого вида. :) X[count(@*) | node()) eq 5]

(: Первый дочерний элемент X при условии, что его значение равно ‘some text’, в противном случае пустая последовательность. :) X[1][. eq ‘some text’]

(: Отобрать всех детей элемента X со значением ‘some text’ и вернуть

первый из них либо пустую последовательность, если таковых не существует.

Проще говоря, вернуть первый дочерний элемент X, имеющий строковое

значение ‘some text’. :)

X[. eq ‘some text’][1]

Обсуждение

Как и в рецепте 1.1, невозможно рассмотреть все интересные комбинации фильтрующих предикатов. Однако, разобравшись с приведенными примерами, вы сможете написать практически любой нужный вам предикат. Отметим также, что с помощью логических операторов and, or и функции not() можно состав­лять и более сложные условия:

number(@a) > 5 and X[number(@a) < 10]

Применяя предикаты со сложными выражениями, не забывайте о скобках.

(: Отобрать первый дочерний элемент с именем Y для каждого дочернего элемента X контекстного узла. Это выражение может вернуть последователь­ность, содержащую несколько элементов Y. :) X/Y[1]

(: Отобрать последовательность узлов X/Y и вернуть первый из них. Это выражение может вернуть не более одного элемента Y. :) (X/Y)[1]

Ученый-теоретик сказал бы, что оператор [] имеет более высокий приоритет, чем оператор /.

Мангано Сэл  XSLT. Сборник рецептов. – М.: ДМК Пресс, СПБ.: БХВ-Петербург, 2008. – 864 с.: ил.

По теме:

  • Комментарии