Получить узлы, которые не имеют определенного предка xml xpath - PullRequest
1 голос
/ 16 мая 2011

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

Если быть точным, у меня есть XML-документ, подобный этому

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

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 вот так

// а [Ь = "CALL_EXPRESSION"] / д / а [1]

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

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

* +1025 * // * [Ь = "NAME"] [нет (предка :: A [B = "CALL_EXPRESSION"] / д / а [1])]

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

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

// * [б = "NAME"] [нет (предок :: а [../../ б = "CALL_EXPRESSION" и позиция () = 1])]

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

// * [б = "NAME"] [нет (предок :: а [Ь = "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>

Ответы [ 2 ]

3 голосов
/ 16 мая 2011

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

//* except //x//*

, чтобы выбрать все элементы, которые не имеют x в качестве предка.

Оператор исключения также может быть смоделирован в XPath 1.0 с использованием эквивалентности

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

(но с учетом контекста для оценки E2).

1 голос
/ 16 мая 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>
...