Условия на рекурсивных XPath

Как я могу использовать рекурсивный И условный выбор в XPath?

Например, учитывая этот документ:

<root xmlns:foo="http://www.foo.org/" xmlns:bar="http://www.bar.org">
  <file name="foo.mp4">
    <chunks>
      <file>
        <chunks>
          <file>
          <chunks>
            <file>1</file>
            <file>2</file>
            <file>3</file>
            <file>4</file>
          </chunks>
          </file>
          <file>
          <chunks>
            <file>5</file>
            <file>6</file>
            <file>7</file>
            <file>8</file>
          </chunks>
          </file>
        </chunks>
      </file>
      <file>
        <chunks>
          <file>
          <chunks>
            <file>9</file>
            <file>10</file>
            <file>11</file>
            <file>12</file>
          </chunks>
          </file>
          <file>
          <chunks>
            <file>13</file>
            <file>14</file>
            <file>15</file>
            <file>16</file>
          </chunks>
          </file>
        </chunks>
      </file>
    </chunks>
  </file>
</root>

Я хотел бы выбрать только:

<file>1</file>
<file>2</file>
<file>3</file>
<file>4</file>

Итак, эффективно это:

//[name="foo.mp4"]/chunks/*[1]/chunks/*[1]/*

Но с обобщенным подходом - то есть что-то, что будет охватывать даже более глубокие вложенные объекты. Что-то вроде этого:

//[name="foo.mp4"]/(chunks/*[1]/)+/*

(cond)+ это не синтаксис XPath, а регулярное выражение того, что я хочу.

2 ответа

Рекурсия подразумевает самоссылку и не доступна напрямую в XPath. Обычный способ игнорировать промежуточные уровни элементов через descendant-or-self ось (//), закрепленный желаемым свойством.

Например, каждое из следующих выражений XPath,

  • Все file элементы со значениями менее 5:

    //file[number() < 5]
    
  • Первые 4 листа file элементы:

    //file[not(*)][count(preceding::file[not(*)]) < 4]
    
  • file листовые элементы, у предков которых нет предшественников:

    //file[not(*)][not(ancestor::*[preceding::*])]
    

выберу

<file>1</file>
<file>2</file>
<file>3</file>
<file>4</file>

как просили.

Насколько я знаю, не существует такого понятия, как рекурсивный XPath. Таким образом, вам нужно объединить XPath с некоторыми другими вещами, такими как XSLT или язык программирования, чтобы иметь возможность выполнять рекурсию. Используя чистый XPath, вам необходимо сформулировать требование по-другому, если это возможно.

Я не знаю, применимо ли это к вашим фактическим данным, но можете ли вы сформулировать требование к чему-то вроде следующего, например:

file[@name='foo.mp4'] найди первое <chunk> который содержит лист <file> т.е. <file> элемент, который не содержит никакого элемента, только текстовые узлы и возвращает лист <file> элементы"

тогда будет возможное чистое решение XPath:

(//file[@name='foo.mp4']//chunks[not(file/*)])[1]/file

данный образец XML в вопросе, ожидаемый результат file 1-4 возвращаются вышеприведенным выражением XPath при тестировании here,

Другие вопросы по тегам