Spring and CastorMarshaller: add namespace to XML root

3.8k views Asked by At

My Java application tries to get information from a webservice. The XML request needs to have the namespace specified in the XML root element (class name), but the namespace of the tags (class fields) need to be empty (null), otherwise the webservice rejects the request.

I have to use Spring 3.0 and Spring WS 2.0 with CastorMarshaller (currently using Castor version 1.3.1) to marshall/unmarshall my Java objects into/from XML.

Please note the __PREFIX__ and __NAMESPACE__ locations in following code snippets.

Desired marshalled output (i.e. the desired generated SOAP request)

<?xml version="1.0" encoding="UTF-8"?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
    <soap-env:Header xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" />
    <soap-env:Body xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
        <__PREFIX__:className xmlns:__PREFIX__="__NAMESPACE__">
            <fieldName>fieldValue</fieldName>
        </__PREFIX__:className>
    </soap-env:Body>
</soap-env:Envelope>

Currently marshalled output (i.e. the generated SOAP request)

Not adding the namespace

<?xml version="1.0" encoding="UTF-8"?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
    <soap-env:Header xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" />
    <soap-env:Body xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
        <className>
            <fieldName>fieldValue</fieldName>
        </className>
    </soap-env:Body>
</soap-env:Envelope>

or adding the namespace to all elements

<?xml version="1.0" encoding="UTF-8"?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
    <soap-env:Header xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" />
    <soap-env:Body xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
        <__PREFIX__:className xmlns:__PREFIX__="__NAMESPACE__">
            <__PREFIX__:fieldName xmlns:__PREFIX__="__NAMESPACE__">fieldValue</__PREFIX__:fieldName>
        </__PREFIX__:className>
    </soap-env:Body>
</soap-env:Envelope>

which are both rejected by the webservice.

My configuration

CastorMarshaller bean in applicationContext.xml

<bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller">
    <property name="mappingLocation" value="classpath:castor-mapping.xml" />
    <property name="ignoreExtraAttributes" value="true" />
    <property name="ignoreExtraElements" value="true" />
    <property name="namespaceMappings">
        <map>
            <entry key="__PREFIX__" value="__NAMESPACE__" />
        </map>
    </property>
</bean>

Castor Mapping file castor-mapping.xml

Not adding the namespace (namespace specified in the castorMarshaller bean through namespaceMappings should be added to the root)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
                         "http://castor.org/mapping.dtd">
<mapping>
    <class name="some.package.ClassName">
        <map-to xml="className">
        <field name="fieldName" type="string">
            <bind-xml name="fieldName" node="element" />
        </field>
    </class>
</mapping>

or adding the namespace to all elements

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
                         "http://castor.org/mapping.dtd">
<mapping>
    <class name="some.package.ClassName">
        <map-to xml="className" ns-uri="__NAMESPACE__" ns-prefix="__PREFIX__">
        <field name="fieldName" type="string">
            <bind-xml name="fieldName" node="element" />
        </field>
    </class>
</mapping>
1

There are 1 answers

0
VirtualTroll On

Since I am facing the same problem, a solution that I am considering is the following:

  1. Create a interceptor extending EndpointInterceptorAdapter
  2. Override handleResponse method
  3. Modify to soap message by directly access or using a transformer

public class MyEndpointInterceptorAdapter extends EndpointInterceptorAdapter{

      @Override
      public boolean handleResponse(MessageContext msgContext, Object endpoint) throws IOException {

          WebServiceMessage responseMsg = msgContext.getResponse();
          SoapMessage soapMsg = (SoapMessage) responseMsg;

          if(soapMsg!=null){
              SoapEnvelope soapEnvelope=soapMsg.getEnvelope();

              if(soapEnvelope!=null){

                  SoapBody soapbody=soapEnvelope.getBody();

                  if(soapbody!=null){

                      Source bodySource=soapbody.getSource();
                      if(bodySource instanceof DOMSource){
                          DOMSource bodyDomSource=(DOMSource)bodySource;
                          Node bodyNode=bodyDomSource.getNode();

                          if(bodyNode!=null){
                              NodeList bodyNodeList=bodyNode.getChildNodes();

                              if(bodyNodeList.getLength()!=0){
                                  Element root=(Element)bodyNodeList.item(0);
                                  root.setAttribute("xmlns:ns", "YourURI");
                                  root.setPrefix("ns");               
                              }
                          }       
                      }
                  }
              }
          }

          return true;
      }

}