Pobieranie surowego XML z SOAPMessage w Javie

79

Skonfigurowałem SOAP WebServiceProvider w JAX-WS, ale mam problem ze znalezieniem surowego kodu XML z obiektu SOAPMessage (lub dowolnego Node). Oto przykład kodu, który mam teraz i gdzie próbuję pobrać 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?
    }
}

Czy istnieje prosty sposób uzyskania kodu XML pierwotnego żądania? Jeśli istnieje sposób na uzyskanie surowego XML przez skonfigurowanie innego typu dostawcy (na przykład źródła), też bym to zrobił.

Dan Lew
źródło

Odpowiedzi:

152

Możesz spróbować w ten sposób.

SOAPMessage msg = messageContext.getMessage();
ByteArrayOutputStream out = new ByteArrayOutputStream();
msg.writeTo(out);
String strMsg = new String(out.toByteArray());
Smith Torsahakul
źródło
4
Nie bierze to pod uwagę kodowania znaków
artbristol
Czy zajmie dużo pamięci przy konstruowaniu obiektów DOM lub podobnych? A może naprawdę poda nieprzetworzony ciąg z odpowiedzi HTTP bez wewnętrznej analizy pliku xml?
Rusłan
22

Jeśli masz SOAPMessagelub SOAPMessageContext, możesz użyć a Transformer, konwertując go na Sourcevia 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());

To weźmie pod uwagę kodowanie, więc Twoje „znaki specjalne” nie zostaną zniekształcone.

artbristol
źródło
Czy dostarczane rozwiązanie zajmuje dużo pamięci?
lospejos
12

Okazuje się, że surowy XML można pobrać za pomocą Provider <Source> w ten sposób:

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

Skończyło się na tym, że nie potrzebowałem tego rozwiązania, więc tak naprawdę nie próbowałem sam tego kodu - może to wymagać pewnych poprawek, aby było dobrze. Ale wiem, że jest to właściwa ścieżka do uzyskania surowego XML z usługi sieciowej.

(Nie jestem pewien, jak to działa, jeśli absolutnie musisz mieć obiekt SOAPMessage, ale z drugiej strony, jeśli mimo wszystko masz zamiar obsługiwać surowy XML, dlaczego miałbyś używać obiektu wyższego poziomu?)

Dan Lew
źródło
A StringWriterjest dobrą alternatywą dla kombinacji ByteArrayOutputStream+ StreamResult, jeśli chcesz, aby XML był Stringz poprawnym kodowaniem
artbristol,
12

tylko w celu debugowania użyj jednego wiersza kodu -

msg.writeTo(System.out);

Shahadat Hossain Khan
źródło
OP niekoniecznie jest debugowaniem w System.out (który niekoniecznie jest wygodny dla serwera WWW) - może on / ona potrzebować wysłać oryginalny XML przez gniazdo, przechowywać go gdzieś lub obliczyć statystyki.
nanofarad
Możesz łatwo napisać do ByteArrayOutputStreamkonwersji na String... wydaje mi się łatwe
vikingsteve
7

Jeśli potrzebujesz sformatować ciąg xml do xml, spróbuj tego:

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")));
Hari
źródło
Czy można wybrać spacje i tabulatory, aby wykonać to formatowanie? Przy okazji wiem, że zakładki nie są wskazane.
Philippe Gioseffi
7

Korzystanie z 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
źródło
3

to działa

 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();
user2900572
źródło
Żadne wyjaśnienie nie jest potrzebne.
Martin Kersten,
0

jeśli masz kod klienta, wystarczy dodać następujące dwie linie, aby uzyskać żądanie / odpowiedź XML. Tutaj _calljestorg.apache.axis.client.Call

String request = _call.getMessageContext().getRequestMessage().getSOAPPartAsString();
String response = _call.getMessageContext().getResponseMessage().getSOAPPartAsString();
ARIJIT
źródło
0

To dość stary wątek, ale ostatnio miałem podobny problem. Dzwoniłem do podrzędnej usługi mydła z usługi odpoczynku i musiałem zwrócić odpowiedź xml pochodzącą z serwera podrzędnego w takiej postaci, w jakiej jest.

W końcu dodałem moduł obsługi SoapMessageContext, aby uzyskać odpowiedź XML. Następnie wstrzyknąłem plik XML odpowiedzi do kontekstu serwletu jako atrybut.

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

Następnie pobrałem ciąg odpowiedzi xml w warstwie usług.

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

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

Nie miałem jeszcze okazji go przetestować, ale to podejście musi być bezpieczne dla wątków, ponieważ używa kontekstu serwletu.

Oguz Demir
źródło