Получить узлы, у которых нет определенного предка xml xpath

Я несколько дней борюсь с довольно сложным xpath, и я не могу его сформулировать. У меня есть синтаксическое дерево из синтаксического анализатора языка C ++, и я хотел бы иметь запрос xpath, который выбирает все имена, которых нет в имени функции.

Чтобы быть конкретным, у меня есть XML-документ, подобный этому

(Весь XML-документ находится в конце вопроса, он довольно большой, я вставляю здесь простой обзор структуры документа) есть четыре типа узлов
a - этот элемент содержит один узел
b - содержит информацию о узел (например, «CALL_EXPRESSION»)
c - содержит фактический текст (например, «printf», имена переменных ...)
d - содержит порядковые номера текущего узла (элементы a)

CALL_EXPRESSION
  DOT_EXPRESSION
    NAME_EXPRESSION
      NAME
    NAME_EXPRESSION
      NAME
  PARAMS
    NAME_EXPRESSION
      NAME

CALL_EXPRESSION
  NAME_EXPRESSION
    NAME
  PARAMS
    NAME_EXPRESSION
      NAME

ASSIGNMENT_EXPRESSION
  NAME_EXPRESSION
    NAME
  NAME_EXPRESSION
    NAME

Я хотел бы сформулировать запрос Xpath, который бы выбрал все ИМЯ, которые не являются потомками CALL_EXPRESSION / * [1]. (Это означает, что я хотел бы выбрать все переменные, а не имена функций).

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

// a [b = "CALL_EXPRESSION"] / d / a [1]

здесь нет проблем. Теперь, если я хочу выбрать все узлы, которые не являются потомками этих узлов. Я бы использовал not (ancestor :: X).

Но вот проблема, если я сформулирую выражение Xpath следующим образом:

// * [b = "ИМЯ"] [не (предок :: a [b = "CALL_EXPRESSION"] / d / a [1])]

он выбирает только узлы, у которых вообще нет a, у которого есть дочерний элемент b = "CALL_EXPRESSION". В нашем примере он выбирает только NAME из поддерева ASSIGNMENT_EXPRESSION.

Я подозревал, что проблема в том, что предок :: берет только первый элемент (в нашем случае a [b = "CALL_EXPRESSION"]) и ограничивает его в соответствии с его предикатом, а далее / отбрасываются. Итак, я изменил запрос xpath следующим образом:

//*[b="NAME" visible[not(ancestor::a[../../b="CALL_EXPRESSION "and position () = 1])]

Кажется, это работает только с более простым CALL_EXPRESSION (без DOT_EXPRESSION). Я подозревал, что путь в [] может относиться только к текущему узлу, а не к потенциальным предкам. Но когда я использовал запрос

// * [b = "ИМЯ"] [не (предок :: a [b = "CALL_EXPRESSION"])]

он работал, как можно было бы предположить (были выбраны все ИМЯ, не имеющие предка CALL_EXPRESSION).

Есть ли способ сформулировать нужный мне запрос? И почему запросы не работают?

Заранее спасибо :)

XML

<a>
 <b>CALL_EXPRESSION</b>
 <c>object.method(a)</c>
 <d>
   <a>
     <b>DOT_EXPRESSION</b>
     <c>object.method</c>
     <d>
       <a>
         <b>NAME_EXPRESSION</b>
         <c>object</c>
         <d>
           <a>
             <b>NAME</b>
             <c>object</c>
             <d>
             </d>
           </a>
         </d>
       </a>
       <a>
         <b>NAME_EXPRESSION</b>
         <c>method</c>
         <d>
           <a>
             <b>NAME</b>
             <c>method</c>
             <d>
             </d>
           </a>
         </d>
       </a>
     </d>
   </a>
   <a>
     <b>PARAMS</b>
     <c>(a)</c>
     <d>
       <a>
         <b>NAME_EXPRESSION</b>
         <c>a</c>
         <d>
           <a>
             <b>NAME</b>
             <c>a</c>
             <d>
             </d>
           </a>
         </d>
       </a>
     </d>
   </a>
 </d>
</a>

<a>
 <b>CALL_EXPRESSION</b>
 <c>puts(b)</c>
 <d>
   <a>
     <b>NAME_EXPRESSION</b>
     <c>puts</c>
     <d>
       <a>
         <b>NAME</b>
         <c>puts</c>
         <d>
         </d>
       </a>
     </d>
   </a>
   <a>
     <b>PARAMS</b>
     <c>(b)</c>
     <d>
       <a>
         <b>NAME_EXPRESSION</b>
         <c>b</c>
         <d>
           <a>
             <b>NAME</b>
             <c>b</c>
             <d>
             </d>
           </a>
         </d>
       </a>
     </d>
   </a>
 </d>
</a>

<a>
 <b>ASSIGNMENT_EXPRESSION</b>
 <c>c=d;</c>
 <d>
   <a>
     <b>NAME_EXPRESSION</b>
     <c>c</c>
     <d>
       <a>
         <b>NAME</b>
         <c>c</c>
         <d>
         </d>
       </a>
     </d>
   </a>
   <a>
     <b>NAME_EXPRESSION</b>
     <c>d</c>
     <d>
       <a>
         <b>NAME</b>
         <c>d</c>
         <d>
         </d>
       </a>
     </d>
   </a>
 </d>
</a>

person tach    schedule 16.05.2011    source источник
comment
Ооо, извините, я не осознавал, что код потеряет отступы и теги xml. Перепрошиваю сюда весь код. Вот структура: pastebin.com/VbRBG5LA, а вот XML-документ: pastebin.com/ajPtqprf. Если бы кто-нибудь мог исправить вопрос, буду признателен.   -  person tach    schedule 16.05.2011
comment
Извините, но непонятно, что именно вы хотите выбрать. Пожалуйста, предоставьте минимально возможный XML-документ (необязательно, чтобы он был одного типа, потому что ваш вопрос кажется достаточно общим) с несколькими уровнями и узлами и определите, какие именно узлы в нем вы хотите выбрать. Пожалуйста, отредактируйте свой вопрос или задайте новый вопрос с таким более простым и точным определением.   -  person Dimitre Novatchev    schedule 16.05.2011
comment
Хороший вопрос, +1. См. Мой ответ для двух выражений XPath, которые показывают, как выбирать узлы, которые не являются потомками данного элемента в XML-документе.   -  person Dimitre Novatchev    schedule 16.05.2011
comment
Я повторил вопрос с более простым примером и без описания. Надеюсь, это поможет stackoverflow.com/q/6012713/754982   -  person tach    schedule 16.05.2011
comment
Да, я ответил несколько минут назад.   -  person Dimitre Novatchev    schedule 16.05.2011


Ответы (2)


Вы не сказали, XPath это 1.0 или 2.0. В XPath 2.0 вы можете использовать оператор except: например

//* except //x//*

для выбора всех элементов, у которых нет x в качестве предка.

Оператор except также можно смоделировать в XPath 1.0, используя эквивалентность

E1 except E2 ==> E1[count(.|E2)!=count(E2)]

(но заботясь о контексте для оценки E2).

person Michael Kay    schedule 16.05.2011

Вопрос не очень ясен, и предоставленный XML не является хорошо сформированным XML-документом.

В любом случае, вот моя попытка ответить, основываясь на моем понимании текста этого вопроса.

У нас есть следующий простой XML-документ:

<t>
 <x>
   <y>
     <z>Text 1</z>
   </y>
 </x>
 <x>
  <y>
    <z> Text 2</z>
  </y>
 </x>
</t>

Мы хотим выбрать все z элементы, которые не являются потомками /t/x[1]

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

/t/z | /t/x[position() > 1]//z

или этот:

//z[not(ancestor::x
             [count(ancestor::*) = 1
            and
              not(preceding-sibling::x)
             ]
        )
    ]

Я определенно рекомендую первое выражение XPath, поскольку оно, очевидно, намного проще, короче и легче для понимания.

Это означает: выбрать всех z дочерних элементов верхнего элемента t документа XML и всех z потомков любого x дочернего элемента верхнего элемента t, который не является первым таким x дочерним элементом (чья позиция среди всех x детей t не 1).

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

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

<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=
  "//z[not(ancestor::x
             [count(ancestor::*) = 1
            and
              not(preceding-sibling::x)
             ]
          )
      ]
  "/>

-------------------

 <xsl:copy-of select="/t/z | /t/x[position() > 1]//z"/>
 </xsl:template>
</xsl:stylesheet>

Когда это преобразование применяется к простому XML-документу (показанному выше), мы видим, что оба выражения выбирают именно нужный элемент z. Результат преобразования:

<z> Text 2</z>

-------------------

 <z> Text 2</z>
person Dimitre Novatchev    schedule 16.05.2011
comment
Мне жаль, что я плохо выразился. Мне нужен узел где угодно в документе. Он не обязательно должен быть потомком узла ‹t›, как в этом примере. На самом деле у меня нет информации о позиции элемента ‹z›, я знаю только, что он не должен быть потомком любого узла, который соответствует // t / x [1]. Переформулировал вопрос, надеюсь, там я более понятен :). stackoverflow.com/q/6012713/754982 - person tach; 16.05.2011
comment
@tach: t/z добавлен для полноты. Если вы уверены, что z может встречаться только в x, вы можете опустить выражение /t/z - person Dimitre Novatchev; 16.05.2011