简体   繁体   中英

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

I have a problem which I cannot solve. I have SOAP response which I get from the web service, then I parse it to String and then pass it to method in which I want to find car by id. I constantly get NPE if I use Node or 0 list length if I use NodeList. As a test, I want to get the first car.

SoapResponse:

<?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>

Here I get 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;
}

Here I parse it to 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();
}

Here I try to search that String with 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();
    }
}

As a final step I call all in one method:

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();
}

When I call System.out.println(result.item(0).getTextContent()); I get:

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$1.handle(BehaviorSkinBase.java:96)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.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.access$1500(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$2(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$3(WinApplication.java:177)
at java.lang.Thread.run(Thread.java:748)

Process finished with exit code 0

Since you are already using SAAJ for your call, why not use the same API to read the response?

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();

If you insist of using XPath, then you can do it like this:

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);

I find the second approach overkill since you can traverse the API by going from element to element.

I've just taken the first Car in the XML sequence like you were trying to do in your question. To search by Id you can loop in the first example and break; when you find your car, or update the XPath expression in the second example respectively.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM