2011-06-24 2 views
3

Добрый день! Предположим, что следующий XML:Элементы выбора Xpath, которые содержат подмножество определенного списка

<store> 
    <book id="b1"></book> 
    <book id="b2"></book> 
</store> 
<store> 
    <book id="b2"></book> 
    <book id="b4"></book> 
</store> 
<booklist> 
    <book id="b1"></book> 
    <book id="b2"></book> 
    <book id="b3"></book> 
</booklist> 

Я хочу написать Xpath запрос, который будет выбирать магазины, которые
имеет все свои книги в Booklist. В моем примере это первый магазин, но не второй.
Я пробовал
//store[./book/@id = /booklist/book/@id]
но он выбирает магазины, которые содержат как минимум 1 общую книгу, а не все из них.
Кроме того, не найдено ни одного предиката типа «содержит» для двух списков узлов, поэтому мне не хватает идей.

Редактировать: Я очень ценю любую помощь, но я бы предпочел решение, которое использует «базовые» возможности Xpath (если существует). Для меня это новый язык. Тем не менее, спасибо всем, кто до сих пор отвечает.

+0

Хороший вопрос +1 – cordsen

+0

Хороший вопрос, +1. См. Мой ответ для кратчайшего и простейшего решения одноразового использования XPath. Также поясняется пояснение. –

ответ

4

Использование:

/*/store[not(book[not(@id = /*/booklist/*/@id)])] 

При нанесении на этот XML-документ (предоставленный, сделанный хорошо образован, окружив его с одним верхним элементом):

<t> 
    <store> 
     <book id="b1"></book> 
     <book id="b2"></book> 
    </store> 
    <store> 
     <book id="b2"></book> 
     <book id="b4"></book> 
    </store> 
    <booklist> 
     <book id="b1"></book> 
     <book id="b2"></book> 
     <book id="b3"></book> 
    </booklist> 
</t> 

разыскиваемых stor элемент выбран:

<store> 
    <book id="b1"/> 
    <book id="b2"/> 
</store> 

Проверка подлинности с использованием XSLT в качестве принимающей XPath:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:template match="/"> 
    <xsl:copy-of select= 
    "/*/store[not(book[not(@id = /*/booklist/*/@id)])]"/> 
</xsl:template> 
</xsl:stylesheet> 

когда это XSLT-преобразование применяется на вышеуказанном документе XML, то разыскиваемый, правильный результат:

<store> 
    <book id="b1"/> 
    <book id="b2"/> 
</store> 

Объяснение:

Выражение:

/*/store[not(book[not(@id = /*/booklist/*/@id)])] 

означает:

Выделить все store элементы (дети верхнего элемента) таким образом, что Eсть даже не один book (ребенок) в которых значение атрибута id является одним из значений атрибутов id для book s (детей) booklist.

+0

Короткое и четкое решение, которое сработало. Спасибо! – TryHarder

+0

@TryHarder: Добро пожаловать. –

0

вам, вероятно, нужно использовать функции, попробуйте следующее:

for $bid in //booklist/book/@id 
    if(every $id in //store/book/@id satisfies $id=$bid) then fn:true() 
    else fn:false() 

не уверен, что это работает, но просто дать вам идею.

+0

Это интересный подход. Поскольку я новичок в Xpath, это мое первое знакомство с функциями в нем. – TryHarder

0

Я хотел бы думать, что это было возможно, используя EXSLT набор расширений

Э.Г.:

//store[count(./book/@id) = count(set:intersection(./book/@id,//booklist/book/@id))] 

Но intersection, кажется, не делать то, что я ожидаю его в Питона lxml.etree

1

Вот решение, которое будет работать

//store[not(book/@id = //store/book[not(@id = //booklist/book/@id)]/@id)] 

применяется к этому XML (магазин name добавил для ясности при тестировании) выбирает магазины 1 & 3

<xml> 
    <store name="1"> 
    <book id="b1"/> 
    <book id="b2"/> 
    </store> 
    <store name="2"> 
    <book id="b2"/> 
    <book id="b4"/> 
    </store> 
    <store name="3"> 
    <book id="b1"/> 
    <book id="b3"/> 
    </store> 
    <store name="4"> 
    <book id="b3"/> 
    <book id="b4"/> 
    </store> 
    <booklist> 
    <book id="b1"/> 
    <book id="b2"/> 
    <book id="b3"/> 
    </booklist> 
</xml> 

Объяснение (два негативы сделать положительным)

//store[not(book/@id = # get me all stores that don't have a book/@id that is in the list below 
//store/book[not(@id = //booklist/book/@id)]/@id) # get me a list of store/book/@id where @id is not in the book list 
] 
+0

Да, это то решение, которое я искал. Спасибо. – TryHarder

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