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ł.

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

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.


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


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


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

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

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

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

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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.