Home > Software engineering >  Remove SOAP envelope
Remove SOAP envelope

Time:10-08

I have an InputStream containing a SOAP message, including the envelope. I don't know the contents of the body beforehand and therefore cannot create a Jaxb annotated class for it.

I've tried many ways, inlcuding a custom SOAPWrapper JaxB Class with XmlAnyElement and other ways. Currently I have this:

private InputStream removeSoapEnvelope(final InputStream inputStream) throws IOException, TransformerException
    {
        final SoapBody body = messageFactory.createWebServiceMessage(inputStream)
            .getSoapBody();
        final Transformer transformer = TransformerFactory.newInstance()
            .newTransformer();
        final DOMResult domResult = new DOMResult();
        transformer.transform(body.getPayloadSource(), domResult);
        final StringWriter writer = new StringWriter();
        transformer.transform(new DOMSource(domResult.getNode()), new StreamResult(writer));
        byte[] barray = writer.toString()
            .getBytes(StandardCharsets.UTF_8);
        return new ByteArrayInputStream(barray);
    }

It seems to work but is horribly inefficient. Is there no short and concise way of achieving this with standard libraries and without regex?

Thanks

CodePudding user response:

Here's a solution using XPath to get the element (pure JaxB? not sure). Takes the document as a regular XML document so it should work for any I guess

FileInputStream fileIS;
fileIS = new FileInputStream(System.getProperty("user.home")   "/tmp/soap.xml");

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();

Document xmlDocument;
xmlDocument = builder.parse(fileIS);

XPath xPath = XPathFactory.newInstance().newXPath();
String expression01 = "//*[local-name()='Body']";
Node currentNode = (Node) xPath.compile(expression01).evaluate(xmlDocument, XPathConstants.NODE);

StringWriter buf = new StringWriter();
Transformer xform = TransformerFactory.newInstance().newTransformer();
xform.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); 
xform.setOutputProperty(OutputKeys.INDENT, "yes");
xform.transform(new DOMSource(currentNode), new StreamResult(buf));
System.out.println(buf.toString());

Result:

<soap:Body>
        
    <incident xmlns="http://example.com">
                
        <Company type="String">Test</Company>
            
    </incident>
    
</soap:Body>

CodePudding user response:

I ended up doing it with regex. All other options are too slow:

private InputStream removeSoapEnvelope(final InputStream inputStream) throws IOException
{
    final String text = new String(inputStream.readAllBytes(), UTF_8);
    final String replace = text.replaceAll("\\s*<\\/?(?:SOAP-ENV|soap):(?:.|\\s)*?>", "");
    File file = File.createTempFile("temp", XML_NS_PREFIX);
    Files.writeString(file.toPath(), replace);
    return new FileInputStream(file);
}
  • Related