Подтвердить что ты не робот

NamespaceContext и использование пространств имен с XPath

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

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

4b9b3361

Ответ 1

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

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, ни вышеперечисленная реализация не реализует полный класс/метод, как определено в документе. Вы можете получить полную, основанную на карте реализацию здесь.

Ответ 2

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

Ответ 3

Я нашел удобную реализацию в Apache WebServices Common Utilities" под названием NamespaceContextImpl.

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

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

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

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

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

Ответ 4

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

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

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

Ответ 5

Если вы используете Jersey 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 вручную, если хотите.