NamespaceContext и использование пространств имен с XPath - PullRequest
15 голосов
/ 27 мая 2009

Для разрешения xpath, включающего пространства имен в Java, требуется использование объекта NamespaceContext, сопоставление префиксов с URL-адресами пространства имен и наоборот. Тем не менее, я не могу найти никакого механизма для получения NamespaceContext, кроме как реализовать его самостоятельно. Это кажется нелогичным.

Вопрос: Есть ли какой-нибудь простой способ получить NamespaceContext из документа, или создать его, или, если это не удалось, вообще отказаться от префиксов и указать xpath с полными именами? 1008 *

Ответы [ 5 ]

12 голосов
/ 28 мая 2009

Можно получить экземпляр NamespaceContext без написания собственного класса. На странице class-use показано, что вы можете получить ее, используя пакет javax.xml.stream .

String ctxtTemplate = "<data xmlns=\"http://base\" xmlns:foo=\"http://foo\" />";
NamespaceContext nsContext = null;

XMLInputFactory factory = XMLInputFactory.newInstance();
XMLEventReader evtReader = factory
    .createXMLEventReader(new StringReader(ctxtTemplate));
while (evtReader.hasNext()) {
  XMLEvent event = evtReader.nextEvent();
  if (event.isStartElement()) {
    nsContext = ((StartElement) event)
        .getNamespaceContext();
    break;
  }
}

System.out.println(nsContext.getNamespaceURI(""));
System.out.println(nsContext.getNamespaceURI("foo"));
System.out.println(nsContext
    .getNamespaceURI(XMLConstants.XMLNS_ATTRIBUTE));
System.out.println(nsContext
    .getNamespaceURI(XMLConstants.XML_NS_PREFIX));

Отказ от префиксов в целом может привести к неоднозначным выражениям - если вы хотите удалить префиксы пространства имен, вам придется изменить формат документа. Создание контекста из документа не обязательно имеет смысл. Префиксы должны совпадать с теми, которые используются в выражении XPath, а не с какими-либо документами, как в этом коде:

String xml = "<data xmlns=\"http://base\" xmlns:foo=\"http://foo\" >"
    + "<foo:value>"
    + "hello"
    + "</foo:value>"
    + "</data>";
String expression = "/stack:data/overflow:value";
class BaseFooContext implements NamespaceContext {
  @Override
  public String getNamespaceURI(String prefix) {
    if ("stack".equals(prefix))
      return "http://base";
    if ("overflow".equals(prefix))
      return "http://foo";
    throw new IllegalArgumentException(prefix);
  }

  @Override
  public String getPrefix(String namespaceURI) {
    throw new UnsupportedOperationException();
  }

  @Override
  public Iterator<String> getPrefixes(
      String namespaceURI) {
    throw new UnsupportedOperationException();
  }
}
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
xpath.setNamespaceContext(new BaseFooContext());
String value = xpath.evaluate(expression,
    new InputSource(new StringReader(xml)));
System.out.println(value);

Ни реализация, возвращаемая API StAX, ни вышеописанная реализация не реализуют полные класс / метода контрактов, как определено в документе. Вы можете получить полную, основанную на карте реализацию здесь .

8 голосов
/ 24 ноября 2009

Я только что работал с использованием xpath и NamespaceContexts. Мне попалась хорошая трактовка вопроса на developerworks .

7 голосов
/ 06 декабря 2010

Я нашел удобную реализацию в " Общие утилиты Apache WebServices ", называемую NamespaceContextImpl.

Вы можете использовать следующую зависимость maven для получения этого класса:

<dependency>
    <groupId>org.apache.ws.commons</groupId>
    <artifactId>ws-commons-util</artifactId>
    <version>1.0.1</version>
</dependency>

Я использую его следующим образом (я знаю, что он построен для саксофона, но после прочтения кода, он o.k):

NamespaceContextImpl nsContext = new NamespaceContextImpl();
nsContext.startPrefixMapping("foo", "my.name.space.com");

Вам не нужно вызывать endPrefixMapping.

1 голос
/ 03 июля 2014

Если вы используете среду Spring, вы можете повторно использовать их реализацию NamespaceContext org.springframework.util.xml.SimpleNamespaceContext

Это такой же ответ, как и ответ Асафа Месика. Так что он не дает вам автоматического NamespaceContext на основе вашего документа. Вы должны построить это самостоятельно. Тем не менее, это помогает вам, потому что, по крайней мере, дает вам реализацию, начинающуюся с.

Когда мы столкнулись с подобной проблемой, сработала и пружина SimpleNamespaceContext, и "Общие утилиты Apache WebServices". Мы хотели избежать зависимости jar от общих утилит Apache WebServices и использовали Spring, потому что наше приложение основано на Spring.

0 голосов
/ 18 сентября 2015

Если вы используете Джерси 2 и имеете только пространство имен XML по умолчанию (xmlns="..."), вы можете использовать SimpleNamespaceResolver :

<?xml version="1.0" encoding="UTF-8"?>
<Outer xmlns="http://host/namespace">
    <Inner />
</Outer>
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
docBuilderFactory.setNamespaceAware(true);
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();

Document document = docBuilder.parse(new File("document.xml"));
String query = "/t:Outer/t:Inner";

XPath xpath = XPathFactory.newInstance().newXPath();
String xmlns = document.getDocumentElement().getAttribute("xmlns");
xpath.setNamespaceContext(new SimpleNamespaceResolver("t", xmlns));

NodeList nodeList = (NodeList) xpath.evaluate(query, document, XPathConstants.NODESET);

//nodeList will contain the <Inner> element

Вы также можете указать xmlns вручную, если хотите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...