Проверка содержимого узла по заданному набору значений - PullRequest
0 голосов
/ 30 июля 2009

Учитывая XML-документ, такой как это:

<root>
    <foo>
        <bar>a</bar>
        <bar>b</bar>
        <bar>c</bar>
    </foo>
    ...
</root>

Как я могу получить все foo -узлы, которые имеют bar -подноды с определенными значениями?

Так, например, если мне нужны все foo -элементы с bar -субэлементами со значениями a и c, я сейчас использую это выражение:

//*/foo[bar/text()='a'][bar/text()='c']

это хорошо, за исключением того, что это становится неуклюжим, если у меня есть больше "bar -ограничений", и я не слишком большой поклонник программно генерируемых выражений XPath :). Я ищу что-то вроде этого (очевидно, неверный синтаксис):

//*/foo[bar/text() in-set('a', 'c')]

Есть идеи?

Ответы [ 5 ]

1 голос
/ 30 июля 2009

Не совсем понятно, хотите ли вы И или ИЛИ там. Ваш пример XPath с двумя фильтрами - это AND (то есть требуется, чтобы foo имел и «a», и «c»), но комментарии к другим ответам, кажется, подразумевают, что вы действительно хотите ИЛИ (любой foo с любым из «a» "или" с "). С XPath 2.0 последнее было бы очень просто:

//foo[bar[. = ('a', 'c')]]

И немного сложнее:

//foo[count(distinct-values(data(bar[. = ('a', 'c')])))) = 2]

или, если вы будете использовать переменные (я покажу синтаксис XQuery, но на практике вы должны использовать API реализации XPath для предоставления значений для внешних переменных):

let $values := ('a', 'c')
return //foo[count(distinct-values(data(bar[. = $values])))) = count($values)]
1 голос
/ 30 июля 2009

Вы можете использовать

//*/foo[bar/text()='a' or bar/text()='c']

В XPath нет оператора «in». Вот список операторов xpath .

0 голосов
/ 31 июля 2009

Вы можете быть хитрым по этому поводу и добавить элемент в ваш документ, например:

<values>
   <value>a</value>
   <value>c</value>
</values>

Тогда этот XPath

/root/foo[count(bar[.=/root/values/value]) = count(/root/values/value)]

найдет все элементы foo, где число bar дочерних элементов, значение которых находится в вашем списке значений, равно количеству значений в этом списке. Это будет работать - , если все элементы bar содержат различные значения.

0 голосов
/ 30 июля 2009

Для выражений «И» вы более или менее застряли с тем, что у вас есть. Хотя я бы написал это так:

//*/foo[bar/text()='a' and bar/text()='c']

Для выражений "ИЛИ" (ваш вопрос по этому вопросу не ясен) вы можете попробовать:

//*/foo[bar[contains(',a,c,', concat(',', text(), ','))]]

Или, более читабельно написано:

//*/foo[
  bar[
    contains(
      ',a,c,', 
      concat(',', text(), ',')
    )
  ]
]

Находит <foo> элементов, которые содержат <bar> элементов, которые сами соответствуют определенному правилу. И правило таково: «текстовое значение (заключенное в запятые) содержится в строке поиска (заключенное в запятые)».

Для текстового значения 'a' и строки поиска 'a,b' вы должны искать ',a,' в ',a,b,'. Поэтому, если какой-либо из внутренних элементов <bar> совпадает, будет выбран внешний элемент <foo>.

Вы должны будете выбрать разделитель, который не может содержаться в значениях, очевидно. Из вашего примера кода я выбрал ',', но подойдет любой действительный символ.

0 голосов
/ 30 июля 2009

Если у вас есть доступ к XPath 2.0, вы можете использовать функцию matches :

//foo[fn:matches(bar, "a|c")]

К сожалению, XPath 2.0 не так широко поддерживается.

Ответ Томалака дал мне идею:

//foo[bar[fn:matches(text(),"a|c")]]

Это может сработать, поскольку он работает не с набором узлов, а с отдельным текстовым узлом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...