Какое выражение XPath находит набор элементов с заданным объявлением пространства имен? - PullRequest
3 голосов
/ 28 февраля 2012

Предположим, у меня есть документ XML с двумя объявлениями пространства имен с префиксом foo, например:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:foo="http://www.foo.com">
  <one>
    <!-- children nodes here -->
  </one>
  <two>
    <!-- children nodes here -->
  </two>
  <three xmlns:foo="http://www.foo.com">
    <!-- children nodes here -->
  </three>
</root>

Я хотел бы оценить выражение XPath (в Java), которое возвращало бы NodeList элементов, которые имеют это объявление пространства имен, а именно узлов root и three. Я не ищу все узлы, где это пространство имен находится в области видимости, только те узлы, у которых есть объявление пространства имен.

Вот Java, который я планирую использовать:

XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xPath = xPathFactory.newXPath();
XPathExpression xPathExpression = null;  
NodeList nodeList = null;
boolean theExpressionWasCompiled = true;
xPathExpression = xPath.compile(xPathStatement); // XPath goes here!
nodeList = (NodeList) xPathExpression.evaluate(document, XPathConstants.NODESET);

Какой XPath мне следует использовать (значение xPathStatement для метода compile())?

Редактировать: XPath 1 или 2 ok.

Окончательное редактирование. Получается, что XPath не может делать именно то, что я хочу (см. Объяснение Димитра ниже, если вам нужны подробности). Лучшее, что я мог сделать, - это оценить XPath несколько раз (по одному разу на объявление пространства имен), чтобы найти каждый элемент с объявлением пространства имен. Я уже знал, сколько раз каждое пространство имен объявляется, поэтому знание того, сколько раз оценивать, не было для меня проблемой. Не супер эффективно, но это работает. Вот XPath, который я использовал, который очень похож на тот, который придумал Димитр (см. Ниже):

//*[namespace::*[local-name() = 'foo']]
     [not
       (parent::node()
         [namespace::*
           [local-name() = 'foo']
         ]
       )
     ]

Благодарю моего друга, Роджера Костелло, за создание XPath, который я использовал.

Ответы [ 2 ]

5 голосов
/ 28 февраля 2012

В моем понимании то, что вы ищете, невозможно с XPath. Модель данных XPath имеет узлы пространства имен, которые находятся в области действия для любого данного узла элемента; в этой модели разбираешь ли ты

<root xmlns:foo="http://example.com/">
  <child>
    <grandchild/>
  </child>
</root>

или

<root xmlns:foo="http://example.com/">
  <child xmlns:foo="http://example.com/">
    <grandchild/>
  </child>
</root>

или

<root xmlns:foo="http://example.com/">
  <child xmlns:foo="http://example.com/">
    <grandchild xmlns:foo="http://example.com/"/>
  </child>
</root>

не имеет значения в модели, доступной для XPath (и XSLT или XQuery), во всех трех случаях все три узла элемента имеют узел пространства имен с локальным именем foo и значением http://example.com/ в области действия.

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

Так что я не думаю, что ваша проблема решаема с помощью XPath. Однако вы можете подождать, пока кто-то вроде Димитра подтвердит или отвергнет мое мнение.

1 голос
/ 28 февраля 2012

Я хотел бы оценить выражение XPath (в Java), которое возвращало бы NodeList элементов, которые имеют это объявление пространства имен, а именно узлов root и three.Я не ищу все узлы, где это пространство имен находится в области видимости, только узлы, которые имеют объявление пространства имен.

Эта информация теряется при разборе - не сохраняется винформационный набор XML, созданный в результате анализа XML-документа и используемый процессором XPath.

Поэтому невозможно использовать XPath, чтобы различать случаи , когда элементимеет узел пространства имен (но он только унаследован и не объявлен повторно) и случай, когда элемент имеет примечание о пространстве имен, и в дополнение к этому он объявляется в элементе.

Единственное исключение из этогоесли элемент является первым в его последовательности ancestor-or-self::*, имеющей это пространство имен.Очевидно, что в этом случае узел пространства имен не унаследован, поэтому он должен быть объявлен для элемента:

//*[namespace::*
      [name() = 'foo' and . = 'http://www.foo.com']
  and
    not(parent::*
         [namespace::*
           [name() = 'foo' and . = 'http://www.foo.com']
         ]
        )
    ]

Это выражение XPath при оценке по предоставленному XMLdocument :

<root xmlns:foo="http://www.foo.com">
    <one>
        <!-- children nodes here -->
    </one>
    <two>
        <!-- children nodes here -->
    </two>
    <three xmlns:foo="http://www.foo.com">
        <!-- children nodes here -->
    </three>
</root>

выбирает элемент с именем root - как и должно быть .

...