NamespaceContext and using namespaces with XPath

后端 未结 5 943
被撕碎了的回忆
被撕碎了的回忆 2020-12-01 13:10

Resolving an xpath that includes namespaces in Java appears to require the use of a NamespaceContext object, mapping prefixes to namespace urls and vice versa.

5条回答
  •  日久生厌
    2020-12-01 13:22

    It is possible to get a NamespaceContext instance without writing your own class. Its class-use page shows you can get one using the javax.xml.stream package.

    String ctxtTemplate = "";
    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));
    

    Forgoing prefixes altogether is likely to lead to ambiguous expressions - if you want to drop namespace prefixes, you'd need to change the document format. Creating a context from a document doesn't necessarily make sense. The prefixes have to match the ones used in the XPath expression, not the ones in any document, as in this code:

    String xml = ""
        + ""
        + "hello"
        + ""
        + "";
    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 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);
    

    Neither the implementation returned by the StAX API nor the one above implement the full class/method contracts as defined in the doc. You can get a full, map-based implementation here.

提交回复
热议问题