从 Java 中的 SOAPMessage 获取原始 XML

我已经在 JAX-WS 建立了一个 SOAP WebServiceProvider,但是我不知道如何从一个 SOAPMessage (或任何 Node)对象获取原始的 XML。下面是我现在得到的代码示例,以及我试图获取 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?
}
}

有没有一种简单的方法来获取原始请求的 XML?如果有办法通过设置不同类型的 Provider (比如 Source)来获取原始 XML,我也愿意这样做。

192507 次浏览

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

You could try in this way.

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

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

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.

for just debugging purpose, use one line code -

msg.writeTo(System.out);

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

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

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

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.