Xpath для элемента с тем же значением атрибута, что и предок

Как мне написать запрос xpath для соответствия элементу <subitem> в приведенном ниже фрагменте XML?

В частности, я хочу сопоставить любой элемент, у которого есть атрибут «имя», который соответствует значению атрибута «имя» корня. Между <root> и <subitem> может быть произвольное количество предков.

<root name="xyz">
  <anything>
    <subitem name="xyz" />
  </anything>
</root>

Моя цель — написать правило PMD для поиска случаев Logger.getLogger(), которые используют класс, отличный от класса, в котором находится оператор.


person bluskies    schedule 28.06.2012    source источник


Ответы (2)


Используйте это выражение XPath:

//*[@name = /*/@name]

При этом выбирается любой элемент (включая верхний элемент с именем root), чей атрибут name имеет то же строковое значение, что и атрибут name верхнего элемента. Когда это выражение XPath сравнивается с предоставленным XML-документом, выбираются два элемента — root и subitem.

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

//*/*[@name = /*/@name]

Проверка на основе XSLT:

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

 <xsl:template match="/">
   <xsl:copy-of select="//*/*[@name = /*/@name]"/>
========
   <xsl:copy-of select="//*[@name = /*/@name]"/>
 </xsl:template>
</xsl:stylesheet>

Когда это преобразование применяется к предоставленному XML-документу:

<root name="xyz">
    <anything>
        <subitem name="xyz" />
    </anything>
</root>

оцениваются два выражения XPath, и выбранные узлы копируются в выходные данные:

<subitem name="xyz"/>
========
   <root name="xyz">
    <anything>
      <subitem name="xyz"/>
    </anything>
   </root>
   <subitem name="xyz"/>
person Dimitre Novatchev    schedule 29.06.2012

//*[@name=(ancestor::*/@name)]

Это должно сработать. Я бы прочитал это как «Найти все узлы, у которых есть предок с одинаковым name».


РЕДАКТИРОВАТЬ (плюс второй, он все равно слишком много выделил)

После редактирования вопроса:

//*[@name=(/@name)]

Это читается как «Найти все узлы, которые имеют то же name, что и корневой узел». Обратите внимание, что корневой узел может быть не таким, как вы думаете (и вы хотите сопоставить самый верхний узел с именем root, а не root), и вам может понадобиться реализовать его как

//*[@name=(/root/@name)] or //*[@name=(/*/@name)]


Это выбирает два элемента, ваши subitem и root, которые не нужны, вы хотите выбрать только узлы, которые не самые верхние (являются потомками root). Спасибо Дмитрию Новачеву за это.

//*/*[@name=(/*/@name)]

person Petr Janeček    schedule 28.06.2012
comment
О, вы отредактировали вопрос, теперь этот запрос найдет больше, чем вы хотите. Буду редактировать. - person Petr Janeček; 29.06.2012
comment
@DimitreNovatchev Правда? Меня это серьезно интересует, так как я использую внутреннюю поддержку IntelliJ XPath и/или xmlstarlet для оценки выражений XPath. и ни один из них не сообщил о каких-либо предупреждениях или ошибках. Было бы здорово иметь инструмент для проверки моих выражений и указания на мои синтаксические ошибки. - person Petr Janeček; 29.06.2012
comment
@Slanec: Хорошо, небольшое исправление - последние два из трех выражений. На самом деле их синтаксис в порядке, но они не выбирают ни одного узла. Причина в том, что узел документа (или узла) / не может иметь никаких атрибутов, а /@* ничего не выбирает. Что касается приличного инструмента обучения XPath, я могу порекомендовать (бесстыдно) свой собственный визуализатор XPath: huttar.net/dimitre/XPV/TopXML-XPV.html - person Dimitre Novatchev; 29.06.2012
comment
Это замена моего предыдущего комментария, в котором говорилось, что все выражения в этом ответе синтаксически незаконны. Все они синтаксически допустимы, а последние два из трех не выбирают ни одного узла. - person Dimitre Novatchev; 29.06.2012