Элементы выбора Xpath, которые содержат подмножество определенного списка - PullRequest
3 голосов
/ 24 июня 2011

Добрый день! Предположим, следующий XML:

<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, который выберет магазины, которые
есть все их книги в списке книг. В моем примере это первый магазин, но не второй.
Я пробовал
//store[./book/@id = /booklist/book/@id]
но он выбирает магазины, которые содержат хотя бы одну общую книгу, а не все.
Кроме того, не найдено ни одного предиката типа «содержит» для двух списков узлов, поэтому у меня нет идей.

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

Ответы [ 4 ]

4 голосов
/ 25 июня 2011

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

/*/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 элементов (дочерних элементов верхнего элемента), чтобы не было даже одного book (дочерний) в них, чье значение атрибута id является одним из id значений атрибутов book s (дочерних элементов) booklist.

1 голос
/ 25 июня 2011

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

//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 голосов
/ 24 июня 2011

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

например:.

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

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

0 голосов
/ 24 июня 2011

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

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

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

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