将 soapMessage XML 解析为 String 并在 Java 中使用 XPath 搜索它

Parse soapMessage XML as String and search it with XPath in Java

我有一个我无法解决的问题。 我有从 Web 服务获得的 SOAP 响应,然后我将其解析为字符串,然后将其传递给我想通过 ID 查找汽车的方法。 如果我使用 Node,我总是得到 NPE,如果我使用 NodeList,我经常得到 0 list length。作为测试,我想得到第一辆车。

肥皂响应:

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <soap:Body>
      <ShowAllCarsResponse xmlns="http://tempuri.org/">
         <ShowAllCarsResult>
            <Car>
               <Id>1</Id>
               <Make>Mercedes-Benz</Make>
               <Model>C63 AMG</Model>
               <YearOfMake>2007</YearOfMake>
               <Horsepower>450</Horsepower>
               <Displacement>6300</Displacement>
            </Car>
            <Car>
               <Id>2</Id>
               <Make>Lexus</Make>
               <Model>LC 500</Model>
               <YearOfMake>2020</YearOfMake>
               <Horsepower>471</Horsepower>
               <Displacement>5000</Displacement>
            </Car>
            <Car>
               <Id>3</Id>
               <Make>Ferrari</Make>
               <Model>488 GTB</Model>
               <YearOfMake>2018</YearOfMake>
               <Horsepower>661</Horsepower>
               <Displacement>3900</Displacement>
            </Car>
            <Car>
               <Id>4</Id>
               <Make>Rimac</Make>
               <Model>Concept Two</Model>
               <YearOfMake>2021</YearOfMake>
               <Horsepower>1914</Horsepower>
               <Displacement>0</Displacement>
            </Car>
         </ShowAllCarsResult>
      </ShowAllCarsResponse>
   </soap:Body>
</soap:Envelope>

这里我得到soapMessage:

private static SOAPMessage createSOAPRequest() throws Exception {
    MessageFactory messageFactory = MessageFactory.newInstance();
    SOAPMessage soapMessage = messageFactory.createMessage();
    SOAPPart soapPart = soapMessage.getSOAPPart();

    String serverURI = "http://www.w3.org/2001/XMLSchema";

    SOAPEnvelope envelope = soapPart.getEnvelope();
    envelope.addNamespaceDeclaration("xsd", serverURI);

    SOAPBody body = soapMessage.getSOAPBody();
    QName bodyName = new QName("http://tempuri.org/", "ShowAllCars" );
    MimeHeaders headers = soapMessage.getMimeHeaders();
    headers.addHeader("SOAPAction", "http://tempuri.org/" + "ShowAllCars");
    soapMessage.saveChanges();

    return soapMessage;
}

这里我解析为String:

private static String parseXmlToString(SOAPMessage soapMessage) {
    final StringWriter sw = new StringWriter();
    try {
        TransformerFactory.newInstance().newTransformer().transform(
                new DOMSource(soapMessage.getSOAPPart()),
                new StreamResult(sw));
    } catch (TransformerException e) {
        throw new RuntimeException(e);
    }
    return sw.toString();
}

在这里,我尝试使用 XPath 搜索该字符串:

private static void findCarById(String xml) {
    XPath xpath = XPathFactory.newInstance().newXPath();
    System.out.println(xml);
    Document document;
    try {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        document = db.parse(new ByteArrayInputStream(xml.getBytes()));
    } catch (ParserConfigurationException | SAXException | IOException e) {
        e.printStackTrace();
        return;
    }
    String expression = "soap:Envelope/soap:Body/ShowAllCarsResponse/ShowAllCarsResult/Car[1]";
    try {
        NodeList result = (NodeList) xpath.compile(expression).evaluate(document, XPathConstants.NODESET);
        System.out.println("RESULT");
        System.out.println(result.item(0).getTextContent());
    } catch (XPathExpressionException e) {
        e.printStackTrace();
    }
}

作为最后一步,我在一个方法中调用所有方法:

private static void createSoapEnvelope() throws Exception {
    SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
    SOAPConnection soapConnection = soapConnectionFactory.createConnection();

    String url = "http://localhost:49883/CarsService.asmx";
    SOAPMessage soapResponse = soapConnection.call(createSOAPRequest(), url);

    String xml = parseXmlToString(soapResponse);
    findCarById(xml);

    soapConnection.close();
}

当我打电话给System.out.println(result.item(0).getTextContent()); 我得到:

java.lang.NullPointerException
at hr.algebra.controllers.SOAPController.findCarById(SOAPController.java:117)
at hr.algebra.controllers.SOAPController.createSoapEnvelope(SOAPController.java:39)
at hr.algebra.controllers.SOAPController.btnSearchPressed(SOAPController.java:125)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:72)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:276)
at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1771)
at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1657)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Node.fireEvent(Node.java:8411)
at javafx.scene.control.Button.fire(Button.java:185)
at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:182)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase.handle(BehaviorSkinBase.java:96)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase.handle(BehaviorSkinBase.java:89)
at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3757)
at javafx.scene.Scene$MouseHandler.access00(Scene.java:3485)
at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:394)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent(GlassViewEventHandler.java:432)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:431)
at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
at com.sun.glass.ui.View.notifyMouse(View.java:937)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null(WinApplication.java:177)
at java.lang.Thread.run(Thread.java:748)

进程已完成,退出代码为 0

既然您已经在使用 SAAJ 进行调用,为什么不使用相同的 API 来读取响应?

SOAPMessage soapResponse = soapConnection.call(createSOAPRequest(), url);

String ns = "http://tempuri.org/";
SOAPBody responseBody = soapResponse.getSOAPBody();

SOAPElement allCarsResponse = (SOAPElement) responseBody.getElementsByTagNameNS(ns, "ShowAllCarsResponse").item(0);
SOAPElement allCarsResult = (SOAPElement) allCarsResponse.getElementsByTagNameNS(ns, "ShowAllCarsResult").item(0);
NodeList allCars = allCarsResult.getElementsByTagNameNS(ns, "Car");

SOAPElement firstCar = (SOAPElement) allCars.item(0);
String id = firstCar.getElementsByTagNameNS(ns, "Id").item(0).getTextContent();

如果你坚持使用XPath,那么你可以这样做:

SOAPMessage soapResponse = soapConnection.call(createSOAPRequest(), url);

NamespaceContext nsContext = new NamespaceContext() {
    @Override
    public Iterator getPrefixes(String namespaceURI) {
        return null;
    }
    @Override
    public String getPrefix(String namespaceURI) {
        return "http://tempuri.org/".equals(namespaceURI) ? "tmp" : null;
    }
    @Override
    public String getNamespaceURI(String prefix) {
        return "tmp".equals(prefix) ? "http://tempuri.org/" : null;
    }
};

SOAPBody responseBody = soapResponse.getSOAPBody();
Document document = responseBody.extractContentAsDocument();
XPath xpath = XPathFactory.newInstance().newXPath();
xpath.setNamespaceContext(nsContext);

XPathExpression expr = xpath
        .compile("/tmp:ShowAllCarsResponse/tmp:ShowAllCarsResult/tmp:Car[1]/tmp:Id");
String id = expr.evaluate(document);

我发现第二种方法有点矫枉过正,因为您可以通过从一个元素到另一个元素来遍历 API。

我刚刚采用了 XML 序列中的第一个 Car,就像您在问题中尝试做的那样。要按 Id 搜索,您可以在第一个示例和 break; 中循环查找您的汽车,或者分别更新第二个示例中的 XPath 表达式。