ChatGPT解决这个技术问题 Extra ChatGPT

Getting Raw XML From SOAPMessage in Java

I've set up a SOAP WebServiceProvider in JAX-WS, but I'm having trouble figuring out how to get the raw XML from a SOAPMessage (or any Node) object. Here's a sample of the code I've got right now, and where I'm trying to grab the XML:

@WebServiceProvider(wsdlLocation="SoapService.wsdl")
@ServiceMode(value=Service.Mode.MESSAGE)
public class SoapProvider implements Provider<SOAPMessage>
{
    public SOAPMessage invoke(SOAPMessage msg)
    {
        // How do I get the raw XML here?
    }
}

Is there a simple way to get the XML of the original request? If there's a way to get the raw XML by setting up a different type of Provider (such as Source), I'd be willing to do that, too.


p
palacsint

You could try in this way.

SOAPMessage msg = messageContext.getMessage();
ByteArrayOutputStream out = new ByteArrayOutputStream();
msg.writeTo(out);
String strMsg = new String(out.toByteArray());

This doesn't take character encoding into account
Will it consume much memory with something like constructing DOM objects or the like? Or will it really give the raw string from the HTTP response without internally parsing the xml?
@artbristol : you can use String class other constructor i.e. String(out.toByteArray(),StandardCharsets.UTF_8) for handling character encoding
a
artbristol

If you have a SOAPMessage or SOAPMessageContext, you can use a Transformer, by converting it to a Source via DOMSource:

            final SOAPMessage message = messageContext.getMessage();
            final StringWriter sw = new StringWriter();

            try {
                TransformerFactory.newInstance().newTransformer().transform(
                    new DOMSource(message.getSOAPPart()),
                    new StreamResult(sw));
            } catch (TransformerException e) {
                throw new RuntimeException(e);
            }

            // Now you have the XML as a String:
            System.out.println(sw.toString());

This will take the encoding into account, so your "special characters" won't get mangled.


Is provided solustion memory-consuming?
c
childno͡.de

It turns out that one can get the raw XML by using Provider, in this way:

import java.io.ByteArrayOutputStream;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.ws.Provider;
import javax.xml.ws.Service;
import javax.xml.ws.ServiceMode;
import javax.xml.ws.WebServiceProvider;

@ServiceMode(value=Service.Mode.PAYLOAD)
@WebServiceProvider()
public class SoapProvider implements Provider<Source>
{
    public Source invoke(Source msg)
    {
        StreamResult sr = new StreamResult();

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        sr.setOutputStream(out);

        try {
            Transformer trans = TransformerFactory.newInstance().newTransformer();
            trans.transform(msg, sr);

            // Use out to your heart's desire.
        }
        catch (TransformerException e) {
            e.printStackTrace();
        }    

        return msg;
    }
}

I've ended up not needing this solution, so I haven't actually tried this code myself - it might need some tweaking to get right. But I know this is the right path to go down to get the raw XML from a web service.

(I'm not sure how to make this work if you absolutely must have a SOAPMessage object, but then again, if you're going to be handling the raw XML anyways, why would you use a higher-level object?)


A StringWriter is a good alternative to the ByteArrayOutputStream+StreamResult combination, if you want the XML as a String with the correct encoding
S
Shahadat Hossain Khan

for just debugging purpose, use one line code -

msg.writeTo(System.out);


The OP is not necessarily debugging to System.out (which isn't necessarily conveniently accessible for a webserver)--he/she might need to send the original XML over a socket, store it somewhere, or calculate its statistics.
You can easily write to a ByteArrayOutputStream convert to String... seems easy to me
S
Sireesh Yarlagadda

Using Transformer Factory:-

public static String printSoapMessage(final SOAPMessage soapMessage) throws TransformerFactoryConfigurationError,
            TransformerConfigurationException, SOAPException, TransformerException
    {
        final TransformerFactory transformerFactory = TransformerFactory.newInstance();
        final Transformer transformer = transformerFactory.newTransformer();

        // Format it
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");

        final Source soapContent = soapMessage.getSOAPPart().getContent();

        final ByteArrayOutputStream streamOut = new ByteArrayOutputStream();
        final StreamResult result = new StreamResult(streamOut);
        transformer.transform(soapContent, result);

        return streamOut.toString();
    }

M
Michael Myers

If you need formatting the xml string to xml, try this:

String xmlStr = "your-xml-string";
Source xmlInput = new StreamSource(new StringReader(xmlStr));
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
transformer.transform(xmlInput,
        new StreamResult(new FileOutputStream("response.xml")));

Is it possible to choose between spaces and tabs to do this formatting? BTW, I know tabs are not indicated.
s
sygi

this works

 final StringWriter sw = new StringWriter();

try {
    TransformerFactory.newInstance().newTransformer().transform(
        new DOMSource(soapResponse.getSOAPPart()),
        new StreamResult(sw));
} catch (TransformerException e) {
    throw new RuntimeException(e);
}
System.out.println(sw.toString());
return sw.toString();

No explanation needed.
A
ARIJIT

if you have the client code then you just need to add the following two lines to get the XML request/response. Here _call is org.apache.axis.client.Call

String request = _call.getMessageContext().getRequestMessage().getSOAPPartAsString();
String response = _call.getMessageContext().getResponseMessage().getSOAPPartAsString();

O
Oguz Demir

It is pretty old thread but recently i had a similar issue. I was calling a downstream soap service, from a rest service, and I needed to return the xml response coming from the downstream server as is.

So, i ended up adding a SoapMessageContext handler to get the XML response. Then i injected the response xml into servlet context as an attribute.

public boolean handleMessage(SOAPMessageContext context) {

            // Get xml response
            try {

                ServletContext servletContext =
                        ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getServletContext();

                SOAPMessage msg = context.getMessage();

                ByteArrayOutputStream out = new ByteArrayOutputStream();
                msg.writeTo(out);
                String strMsg = new String(out.toByteArray());

                servletContext.setAttribute("responseXml", strMsg);

                return true;
            } catch (Exception e) {
                return false;
            }
        }

Then I have retrieved the xml response string in the service layer.

ServletContext servletContext =
                ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getServletContext();

        String msg = (String) servletContext.getAttribute("responseXml");

Didn't have chance to test it yet but this approach must be thread safe since it is using the servlet context.