2016-10-05 3 views
4

Данный XML:XPath 3.0: выберите узлы по условию (найти максимум дочерних узлов по дате)

<?xml version="1.0" encoding="UTF-8"?> 
<root> 
    <c cid="c0" v="100"> 
     <d did="c00" v="0" dt="2016-01-01" /> 
     <d did="c01" v="0" dt="2016-02-03" /> 
     <d did="c02" v="0" dt="2016-01-15" /> 
    </c> 
    <c cid="c1" v="100"> 
     <d did="c10" v="1" dt="2016-01-01" /> 
     <d did="c11" v="0" dt="2015-03-03" /> 
     <d did="c12" v="0" dt="2015-04-15" /> 
    </c> 
</root> 

я должен найти XPath 2.0 или 3.0, который будет возвращать мне список c узлов с помощью выражения: найти максимум d узел этого c детей по атрибуту dt (проанализируйте его как date), и если его значение атрибута v равно «0», верните узел c.

Я ожидаю получить один узел c (с @ cid = "c0") в результате, потому что /root/c[cid="c1"]/d[@dt="2016-01-01"]/@v не равно 0 (равно 1).

Я застрял на этом XPath ГНФАР:

/root/c[d[max(xs:date(@dt))]/@v="0"] 

Это дает мне ошибку (я использую Oxygen XML Editor, XPath 3.0):

XPath не удалось из-за: Эффективное булево значение не определено для последовательности, начиная с атомным значением, отличным от булевой, номер, строки или URI

Я полагаю, что это связано с неправильным использованием функции max, но не может заставить ее работать.

ОБНОВЛЕНИЕ:

  1. Давайте предположим, что c узлы будут иметь d детей с уникальным максимальным значением их dt атрибутов.
  2. Я также попытался XPath 3.0 выражение:

    /root/c[d[@dt=max(.//@dt/xs:date(.))]/@v="0"]

но он вернулся мне все узлы (неверный результат).

+2

Что делать, если узел c будет иметь более одного узла d с таким же максимальным значением атрибута dt и разными значениями атрибутов v? –

ответ

2

Вот более короткий и, вероятно, более эффективным (максимальная в виде <c> элемента вычисляется только один раз) XPath 2.0 однострочник:

/*/c[max(d/xs:date(@dt)) = d[@v eq '0']/@dt] 

Наслаждайтесь!

+0

BTW, когда я использовал знак равенства '=' вместо 'eq' literal в этом выражении в редакторе Oxygen 18 XPath 3, он также отлично работал. – lospejos

+1

@lospejos, Да, но в XPath 2.0 оператор 'eq' специально разработан для обработки сопоставлений между значениями атомов. Цель общего оператора сравнения '=' должна использоваться в сравнениях, где по крайней мере один из операндов является последовательностью. –

2

В XQuery запрос является довольно простым и может быть записана только рассчитать максимальную дату раз в c узле:

for $c in /root/c 
let $max := max($c/d/@dt/xs:date(.)) 
where $c/d[@dt = $max]/@v = '0' 
return $c 

В XPath вы можете написать запрос (по крайней мере, концептуально) вычислить его один раз в d узел и надеюсь, что результат либо оптимизирован процессор или достаточно быстро в любом случае:

/root/c[d[@dt = max(../d/@dt/xs:date(.))]/@v = '0'] 

Вы также можете использовать let expression в XPath для восстановления явного предварительного вычисления:

/root/c[ 
    let $max := max(d/@dt/xs:date(.)) 
    return d[@dt = $max]/@v = '0' 
] 
+1

Leo Wörteler, на самом деле есть выражение XPath 2.0, которое вычисляет максимум один раз за элемент ''. –

+1

Вы можете злоупотреблять выражением 'for',' some' или 'every', чтобы перебирать один элемент и, таким образом, эмулировать' let', это то, о чем вы говорите? ** Изменить: ** О, я вижу. Очень хорошо! –

 Смежные вопросы

  • Нет связанных вопросов^_^