I'm working on a Spring Boot project where I need to consume a SOAP web service. I used wsdl2java
to generate the client code. However, I'm encountering issues with the generated SOAP request message structure.
Here is the expected SOAP request from SOAP-UI:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:uri="http://uri.org/">
<soapenv:Header/>
<soapenv:Body>
<uri:GetPolicy>
<uri:policyId>12345</uri:policyId>
</uri:GetPolicy>
</soapenv:Body>
</soapenv:Envelope>
However, my Spring Boot application generates the following SOAP request:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns3:GetPolicy xmlns:ns2="http://schemas.data.org/PolicyServicesLibrary" xmlns:ns3="http://uri.org/" xmlns:ns4="http://schemas.microsoft.com/Serialization/Arrays" xmlns:ns5="http://schemas.microsoft.com/Serialization/">
<ns3:policyId>12345</ns3:policyId>
</ns3:GetPolicy>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The differences include are as follows, when I am trying I am getting an error because of the incorrect request SOAP payload.
soapenv
vs SOAP-ENV
).Here's my GetPolicy
class:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"policyId"
})
@XmlRootElement(name = "GetPolicy")
public class GetPolicy {
@XmlElementRef(name = "policyId", namespace = "http://uri.org/", type = JAXBElement.class, required = false)
protected JAXBElement<String> policyId;
public JAXBElement<String> getPolicyId() {
return policyId;
}
public void setPolicyId(JAXBElement<String> value) {
this.policyId = value;
}
}
And, here is the configuration and code where I execute the request:
@Autowired
private WebServiceTemplate webServiceTemplate;
ObjectFactory factory = new ObjectFactory();
GetPolicy request = factory.createGetPolicy();
request.setPolicyId(factory.createGetPolicyPolicyId("12345"));
webServiceTemplate.setMarshaller(jaxb2Marshaller);
webServiceTemplate.setUnmarshaller(jaxb2Marshaller);
webServiceTemplate.setDefaultUri("SOAP_SERVER_URL");
JAXBElement<GetPolicyResponse> response = (JAXBElement<GetPolicyResponse>) webServiceTemplate.marshalSendAndReceive(request, new SoapActionCallback("SOAP_ACTION"));
return response.getValue();
I have also ensured the correct package-level namespace definition in package-info.java
:
@jakarta.xml.bind.annotation.XmlSchema(
namespace = "http://uri.org/",
elementFormDefault = jakarta.xml.bind.annotation.XmlNsForm.QUALIFIED
)
package my.package.name;
Despite these configurations, the generated SOAP request has the incorrect namespace prefixes and includes additional namespaces. Also, I know that by adding XmlNs
I can fix the prefix issue, but I have multiple namespaceURI
which should be matched to the same prefix
. That's where I stuck with.
Questions:
soapenv
as the namespace prefix instead of SOAP-ENV
?Thank you. Any help would be greatly appreciated!
To your first question, to change the namespace prefixes (SOAP-ENV to soapenv) you need to implement the ClientInterceptor and set it to the WebServiceTemplate
.
import jakarta.xml.soap.*;
import lombok.SneakyThrows;
import org.springframework.ws.client.WebServiceClientException;
import org.springframework.ws.client.support.interceptor.ClientInterceptor;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
public class Interceptor implements ClientInterceptor {
@SneakyThrows
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
changePrefix(messageContext);
return true;
}
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
// if you need to change in the response call the method 'changePrefix' here
return true;
}
public boolean handleFault(MessageContext messageContext) throws WebServiceClientException {
// if you need to change in the fault response call the method 'changePrefix' here
return true;
}
public void afterCompletion(MessageContext messageContext, Exception e) throws WebServiceClientException {
}
private void changePrefix(MessageContext messageContext) throws SOAPException {
SaajSoapMessage soapMessage = (SaajSoapMessage) messageContext.getRequest();
SOAPEnvelope soapEnvelope = soapMessage.getSaajMessage().getSOAPPart().getEnvelope();
SOAPHeader soapHeader = soapEnvelope.getHeader();
SOAPBody soapBody = soapEnvelope.getBody();
SOAPFault soapFault = soapBody.getFault();
soapEnvelope.removeNamespaceDeclaration(soapEnvelope.getPrefix());
soapEnvelope.addNamespaceDeclaration("soapenv", soapEnvelope.getNamespaceURI());
soapEnvelope.setPrefix("soapenv");
if (soapHeader != null) soapHeader.setPrefix("soapenv");
if (soapBody != null) soapBody.setPrefix("soapenv");
if (soapFault != null) soapFault.setPrefix("soapenv");
}
}
To set this to webServiceTemplate
you can create a Bean or whatever method you like, add the created interceptor to the new ClientInterceptor[]
array,
webServiceTemplate.setInterceptors(new ClientInterceptor[]{interceptor});
To your second question yes you can use the package-info.java
and add XmlNs
when you need to change the other prefixes in the Soap Body for namespaceURI
.