【问题标题】:XPath query in Java is not returning any elementJava中的XPath查询不返回任何元素
【发布时间】:2019-07-06 04:30:10
【问题描述】:

我正在开发一个使用 SOAP 作为通信协议的 Web 服务:我通过使用 Spring Boot 和 Spring WS 来实现这一点。
有时,我可能需要通过请求的标头发送一些内容,并且我希望能够通过 XPath 表达式恢复该信息。
这是我到目前为止所做的:

        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        WebServiceMessage message = messageContext.getRequest();

        Source s = messageContext.getRequest().getPayloadSource();
        messageContext.getRequest().writeTo(buffer);
        String payload = buffer.toString(java.nio.charset.StandardCharsets.UTF_8.name());
        System.out.println(payload);
        InputStream is = new ByteArrayInputStream(payload.getBytes());

在下面的代码 sn-p 中,我阅读了 get 后端接收的整个请求。之后,我以前做的是将所有这些转换为 SOAPHeader 对象,该对象包含我想要的所有内容......除了我根本无法进行任何 XPath 查询以及我真正需要的元素下树。
所以,我决定用下面的方式继续那个sn-p的代码

        //-------
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(new java.io.ByteArrayInputStream(payload.getBytes()));
        XPath xpath = XPathFactory.newInstance().newXPath();

现在,这是我发送的请求的示例

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:it="it.niuma.mscsoapws.ws">
   <soapenv:Header>
       <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
       <wsse:UsernameToken wsu:Id="UsernameToken-3967AEB46D733EF6E2154990461080350">
       <wsse:Username>TAVI</wsse:Username>
       <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">password</wsse:Password>
       <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">pUn8VjdpVaIamSAIwXEeXg==</wsse:Nonce>
       <wsu:Created>2019-02-11T17:03:30.803Z</wsu:Created>
       </wsse:UsernameToken></wsse:Security>
  </soapenv:Header>
   <soapenv:Body>
      <it:getPOrderRequest>
         <it:poNumber>2197111225-F03292</it:poNumber>
      </it:getPOrderRequest>
   </soapenv:Body>
</soapenv:Envelope>

我想推断的是soapenv:Header 中的整个部分,所以我使用以下XPath 表达式:

        XPathExpression expr = xpath.compile("//*//soapenv:Header");
        Object result = expr.evaluate(doc, XPathConstants.NODESET);
        NodeList nodes = (NodeList) result;
        for (int i = 0; i < nodes.getLength(); i++) {
            Node currentItem = nodes.item(i);
            System.out.println("found node -> " + currentItem.getLocalName() + " (namespace: " + currentItem.getNamespaceURI() + ")");
        }

这是我的问题:Java 端的 XPath 表达式不返回任何内容,而其他工具(如 this one)正确评估表达式并返回一些内容。 在 Java 中唯一能正确计算的 XPath 表达式是 //*,仅此而已。 我错过了什么?

编辑:在@Wisthler 的建议之后,现在对 XPath 进行了评估,但它未能返回某些内容。附上现在发生的情况的屏幕截图:如果我理解正确的话,看看节点变量(应该包含一个 NodesList)是如何为空的

【问题讨论】:

  • @Rathnayake 如果您查看我发布的代码,它的一半灵感来自该资源,但即使添加了 namespaceuri 部分,它仍然不会返回任何内容

标签: java xml web-services xpath soap


【解决方案1】:

除了@Wisthler 的回答之外,这是使一切正常工作所缺少的唯一代码行

        factory.setNamespaceAware(true);

所以整个工作代码是

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(new java.io.ByteArrayInputStream(payload.getBytes()));
        XPath xpath = XPathFactory.newInstance().newXPath();
        xpath.setNamespaceContext(new NamespaceContext() {
            @Override
            public String getNamespaceURI(String prefix) {
                if("soapenv".equals(prefix)){
                    return "http://schemas.xmlsoap.org/soap/envelope/";
                }else{
                    return null;
                }
            }

            @Override
            public String getPrefix(String namespaceURI) {
                return null;
            }

            @Override
            public Iterator getPrefixes(String namespaceURI) {
                return null;
            }
        });
        XPathExpression expr = xpath.compile("//soapenv:Envelope//soapenv:Header//text()[normalize-space()]");
        Object result = expr.evaluate(doc, XPathConstants.NODESET);
        NodeList nodes = (NodeList) result;

谢谢@Wistler!

【讨论】:

    【解决方案2】:

    我知道上次我在 java 中使用 XPath 时遇到了命名空间问题,不得不添加一个 namespaceContext 以使其工作。

    如果你想在 XPath 中使用soapenv 命名空间,你需要“注册”它。 你可以在下面找到我当时所做的事情。可能不是 100% 干净,但仍可能对您有所帮助

        xpath.setNamespaceContext(new NamespaceContext() {
            @Override
            public String getNamespaceURI(String prefix) {
                if("soapenv".equals(prefix)){
                    return "http://schemas.xmlsoap.org/soap/envelope/";
                }else{
                    return null;
                }
            }
    
            @Override
            public String getPrefix(String namespaceURI) {
                return null;
            }
    
            @Override
            public Iterator getPrefixes(String namespaceURI) {
                return null;
            }
        });
    

    编辑:使用我对您的代码的修复进行了测试运行,这是您期望看到的吗?

    found node -> Header (namespace: http://schemas.xmlsoap.org/soap/envelope/)
    

    【讨论】:

    • 类似的,是的。该行仅用于调试目的。让我测试一下……
    • 嗯,不,我还有一个问题。现在评估不返回 null 但如果我尝试进入 for 循环应用程序崩溃
    • 哪个循环?你能提供更多细节吗?
    • 当然,我在原帖中添加了一张图片!
    • 找到了解决方案:如果您好奇,请阅读我的回答 :-) 非常感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-01
    • 1970-01-01
    • 2021-11-15
    相关资源
    最近更新 更多