92

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.

Dan Lew
  • 85,990
  • 32
  • 182
  • 176

9 Answers9

171

You could try in this way.

SOAPMessage msg = messageContext.getMessage();
ByteArrayOutputStream out = new ByteArrayOutputStream();
msg.writeTo(out);
String strMsg = new String(out.toByteArray());
palacsint
  • 28,416
  • 10
  • 82
  • 109
Smith Torsahakul
  • 1,719
  • 2
  • 10
  • 2
  • 4
    This doesn't take character encoding into account – artbristol Nov 19 '13 at 17:35
  • 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? – Ruslan Mar 28 '16 at 20:38
  • @artbristol : you can use String class other constructor i.e. String(out.toByteArray(),StandardCharsets.UTF_8) for handling character encoding – Ashish Kathait Jan 06 '22 at 15:02
  • 1
    Sigh. 166 upvotes so far. Please *don't* use this answer. You will mangle your 'special characters'. You can't use the String constructor idea suggested by Ashish, because you would need to specify the character encoding in the source code at compile time, but you don't know it until runtime. Please use my answer. – artbristol Mar 07 '23 at 10:38
  • Just what I needed! Gonna save it for future use. – Alain Cruz Apr 04 '23 at 15:32
28

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.

artbristol
  • 32,010
  • 5
  • 70
  • 103
13

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. – nanofarad Sep 11 '14 at 10:19
  • You can easily write to a `ByteArrayOutputStream` convert to `String`... seems easy to me – vikingsteve Aug 17 '16 at 12:38
13

It turns out that one can get the raw XML by using Provider<Source>, 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?)

childno͡.de
  • 4,679
  • 4
  • 31
  • 57
Dan Lew
  • 85,990
  • 32
  • 182
  • 176
  • 1
    A `StringWriter` is a good alternative to the `ByteArrayOutputStream`+`StreamResult` combination, if you want the XML as a `String` with the correct encoding – artbristol Nov 20 '13 at 08:51
10

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();
    }
Sireesh Yarlagadda
  • 12,978
  • 3
  • 74
  • 76
7

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")));
Michael Myers
  • 188,989
  • 46
  • 291
  • 292
Hari
  • 71
  • 1
  • 1
3

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();
sygi
  • 4,557
  • 2
  • 32
  • 54
user2900572
  • 571
  • 1
  • 8
  • 21
0

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();
ARIJIT
  • 676
  • 5
  • 7
0

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.

Oguz Demir
  • 169
  • 2
  • 6