在Java中获取SOAPMessage的原始XML

92

我已经在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?如果可以通过设置不同类型的提供程序(如Source)来获取原始XML,我也愿意这样做。

9个回答

171

你可以尝试这种方式。

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

4
这并未考虑字符编码。 - artbristol
它是否会占用大量内存,例如构建DOM对象之类的操作?或者它是否真的会直接返回HTTP响应中的原始字符串,而不进行内部解析XML? - Ruslan
@artbristol:你可以使用String类的其他构造函数,例如String(out.toByteArray(), StandardCharsets.UTF_8),来处理字符编码。 - Ashish Kathait
1
叹息。到目前为止有166个赞。请不要使用这个答案。你会搞乱你的“特殊字符”。你不能使用Ashish建议的字符串构造函数方法,因为你需要在编译时在源代码中指定字符编码,但直到运行时你才知道它。请使用我的答案。 - artbristol
正是我所需要的!将它保存下来以备将来使用。 - Alain Cruz

28
如果您有一个SOAPMessageSOAPMessageContext,可以通过将其转换为DOMSource,然后使用Transformer来处理。
            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());
这将考虑编码,因此您的“特殊字符”不会被搞乱。

提供的解决方案是否占用大量内存? - lospejos

13
仅用于调试目的,使用以下一行代码 - msg.writeTo(System.out);
该行代码可以将消息输出到系统控制台,以便进行调试。

OP 不一定要调试到 System.out(对于 Web 服务器来说并不一定方便访问)- 他/她可能需要通过套接字发送原始 XML,将其存储在某个地方或计算其统计信息。 - nanofarad
你可以轻松地向ByteArrayOutputStream写入数据并转换成String...对我来说似乎很容易。 - vikingsteve

13
使用Provider<Source>,可以获取原始XML,示例如下:
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;
    }
}

我最终没有需要这个解决方案,所以我实际上没有尝试过这段代码 - 它可能需要一些微调才能正确运行。 但是我知道这是获取来自Web服务的原始XML的正确方法。

(如果您绝对必须使用SOAPMessage对象,我不确定如何使其工作,但再说一遍,如果您将处理原始XML,为什么要使用更高级别的对象?)


1
如果您想要将XML作为带有正确编码的字符串,那么StringWriterByteArrayOutputStream+StreamResult组合的一个很好的替代方案。 - artbristol

10

使用Transformer工厂:

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

7
如果您需要将XML字符串格式化为XML,请尝试以下方法:
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")));

可以选择使用空格还是制表符进行格式化吗?顺便说一句,我知道制表符并不被推荐使用。 - Philippe Gioseffi

3

这个有效

 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. - Martin Kersten

0
如果你有客户端代码,那么你只需要添加以下两行代码就可以获取XML请求/响应。这里的_callorg.apache.axis.client.Call
String request = _call.getMessageContext().getRequestMessage().getSOAPPartAsString();
String response = _call.getMessageContext().getResponseMessage().getSOAPPartAsString();

0

这是一个相当老的线程,但最近我遇到了类似的问题。我正在从REST服务调用下游SOAP服务,并且需要原样返回来自下游服务器的XML响应。

因此,我最终添加了一个SoapMessageContext处理程序来获取XML响应。然后,我将响应XML作为属性注入到servlet上下文中。

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

然后我在服务层中检索了XML响应字符串。

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

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

虽然我还没有测试过,但这种方法一定是线程安全的,因为它使用了servlet上下文。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接