Search code examples
soapjaxbjax-wswebservice-clientwsimport

JAX-WS client, incorrect soap request - The markup in the document following the root element must be well-formed


I have generated a web service client using wsimport command line (JAX-WS) and generated Java objects using JAXB and separate XSD files.

But when I try to request, it gives following error from server side.

SAXException, cause: The markup in the document following the root element must be well-formed.

So I checked my soap request with a sample working soap message. Then I realized that there is a different between these two messages.

1. Sample working message.

<m:getMyDetail xmlns:m="http://axis.frontend.hi.example.net"> 
<MyDetailRQ xmlns="http://www.example.net/schemas/1005/06/messages" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://www.example.net/1005/06/messages ../xsd/MyDetailRQ.xsd" 
version="2013/12">

2. Web service client generated soap message.

<ns3:getMyDetail xmlns:ns3="http://axis.frontend.hi.example.net" xmlns="http://www.  
example.net/schemas/1005/06/messages" xmlns:ns2="http://www. example.net/wsdl/1005/06"  
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="MyDetailRQ">

As you can see, above expected MyDetailRQ wrapper tag element is missing but it generated as xsi:type="MyDetailRQ" attribute.

I’m not sure why this happen and how to fix this through my client project configurations.

Appreciate your help and advice.

Thanks

- Relevant WSDL part

<wsdl:types>
    <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://axis.frontend.h1.example.com">
        <element name="getMyDetail" type="xsd:anyType"/>
    </schema>
    <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.com/wsdl/2005/06">
        <element name="getMyDetailReturn" type="xsd:anyType"/>
    </schema>
</wsdl:types>
<wsdl:message name="getMyDetailRequest">
    <wsdl:part element="tns1:getMyDetail" name="part"/>
</wsdl:message>
<wsdl:message name="getMyDetailResponse">
    <wsdl:part element="impl:getMyDetailReturn" name="getMyDetailReturn"/>
</wsdl:message>
<wsdl:portType name="MyService">
    <wsdl:operation name="getMyDetail">
        <wsdl:input message="impl:getMyDetailRequest" name="getMyDetailRequest"/>
        <wsdl:output message="impl:getMyDetailResponse" name="getMyDetailResponse"/>
    </wsdl:operation>
</wsdl:portType>
<wsdl:binding name="MyServiceSoapBinding" type="impl:MyService">
    <wsdl:operation name="getMyDetail">
        <wsdlsoap:operation soapAction=""/>
        <wsdl:input name="getMyDetailRequest">
            <wsdlsoap:body use="literal"/>
        </wsdl:input>
        <wsdl:output name="getMyDetailResponse">
            <wsdlsoap:body use="literal"/>
        </wsdl:output>
    </wsdl:operation>
</wsdl:binding>
<wsdl:service name="MyServiceService">
    <wsdl:port binding="impl:MyServiceSoapBinding" name="MyService">
        <wsdlsoap:address location="http://interface.example.com/xmls/ws/MyService"/>
    </wsdl:port>
</wsdl:service>

- Relevant XSD part

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns="http://www.example.net/schemas/1005/06/messages" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.net/schemas/1005/06/messages" elementFormDefault="qualified" attributeFormDefault="unqualified">
    <xs:include schemaLocation="MyCommonTypes.xsd"/>
    <xs:element name="MyDetailRQ">

- Relevant Java Object

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "MyDetailRQ", propOrder = {
    "myCode"
})
@XmlRootElement(name = "MyDetailRQ")
public class MyDetailRQ
    extends MainRequest
{
}

- web service client - service interface (Updated on 17-Oct-2014)

    @WebService(name = "MyService", targetNamespace = "http://www.example.net/wsdl/1005/06")
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
@XmlSeeAlso({
    com.myp.hi.frontend.axis.ObjectFactory.class,
        com.myp.ObjectFactory.class
})
public interface MyService {
    /**
         * 
         * @param part
         * @return
         *     returns java.lang.Object
         */
        @WebMethod
        @WebResult(name = "getMyDetailReturn", targetNamespace = "http://www.example.net/wsdl/1005/06", partName = "getMyDetailReturn")
        public Object getMyDetail(
            @WebParam(name = "getMyDetail", targetNamespace = "http://axis.frontend.hi.example.net", partName = "part")
            Object part);

}

- ObjectFactory class (Updated on 17-Oct-2014)

@XmlRegistry
public class ObjectFactory {

    private final static QName _GetMyDetail_QNAME = new QName("http://axis.frontend.hi.example.net", "getMyDetail");


    public ObjectFactory() {
    }

    @XmlElementDecl(namespace = "http://axis.frontend.hi.example.net", name = "getMyDetail")
            public JAXBElement<Object> createGetMyDetail(Object value) {
            return new JAXBElement<Object>(_GetMyDetail_QNAME, Object.class, null, value);
    }


 }

-MyServiceService class (Updated on 17-Oct-2014)

@WebServiceClient(name = "MyServiceService", targetNamespace = "http://www.example.net/wsdl/1005/06", wsdlLocation = "http://interface.example.com/xmls/ws/MyService?wsdl")
@GZIP
public class MyServiceService
    extends Service
{

    private final static URL MYSERVICESERVICE_WSDL_LOCATION;
    private final static WebServiceException MYSERVICESERVICE_EXCEPTION;
    private final static QName MYSERVICESERVICE_QNAME = new QName("http://www.example.net/wsdl/1005/06", "MyServiceService");

    static {
        URL url = null;
        WebServiceException e = null;
        try {
            url = new URL("http://interface.example.com/xmls/ws/MyService?wsdl");
        } catch (MalformedURLException ex) {
            e = new WebServiceException(ex);
        }
        MYSERVICESERVICE_WSDL_LOCATION = url;
        MYSERVICESERVICE_EXCEPTION = e;
    }

 @WebEndpoint(name = "MyService")
    public MyService getMyService() {

        MyService port = super.getPort(new QName("http://www.example.net/wsdl/1005/06", "MyService"), MyService.class);
        return port;
    }

 private static URL __getWsdlLocation() {
        if (MYSERVICESERVICE_EXCEPTION!= null) {
            throw MYSERVICESERVICE_EXCEPTION;
        }
        return MYSERVICESERVICE_WSDL_LOCATION;
    }


}

- My Test class (Updated on 17-Oct-2014)

MyService myService = new MyServiceService().getMyService();

MyDetailRQ myDetailRQ = new MyDetailRQ();


myDetailRQ.setCredentials(credentials);
myDetailRQ.setLanguage("ENG");
myDetailRQ.setMyCode("52319");

MyDetailRS myDetailRS = (MyDetailRS) myService.getMyDetail(myService);

Solution

  • I have found a solution, but not sure whether perfect one.

    What I have done is; pass a XML String to service and get response as a String from service. Then manually do marshalling and un- marshalling.

    See the example below.

    MyService myService = new MyServiceService().getMyService();
    
    MyDetailRQ myDetailRQ = new MyDetailRQ();
    
    
    myDetailRQ.setCredentials(credentials);
    myDetailRQ.setLanguage("ENG");
    myDetailRQ.setMyCode("52319");
    
    String requestStr = getStringFromJaxb(myDetailRQ.class, myDetailRQ);
    
    String responseStr = (String) myService.getMyDetail(requestStr);
    
    MyDetailRS myDetailRS = (MyDetailRS) getJaxbFromString(MyDetailRS.class, responseStr);
    
    
    
    
    private static Object getJaxbFromString(Class<?> clazz, String xmlString) {
            StringReader input = null;
            Object o = null;
            try {
                input = new StringReader(xmlString);
                JAXBContext context = JAXBContext.newInstance(clazz);
                Unmarshaller um = context.createUnmarshaller();
                o = um.unmarshal(input);
                if (o instanceof JAXBElement)
                    o = ((JAXBElement<?>) o).getValue();
            } catch (JAXBException e) {
                e.printStackTrace();
            } finally {
                if (input != null)
                    input.close();
            }
            return o;
        }
    
    
    
    /**
         * Helper method to get xml string from JAXB Object.
         * @param clazz
         * @param o
         * @return
         */
        private static String getStringFromJaxb(Class<?> clazz, Object o) {
            String theXML = "";
            try {
                StringWriter writer = new StringWriter();
                JAXBContext context = JAXBContext.newInstance(clazz);
                Marshaller m = context.createMarshaller();
                m.setProperty("com.sun.xml.bind.xmlDeclaration", Boolean.FALSE);
                m.marshal(o, writer);
    
                // output string to console
                theXML = writer.toString();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return theXML;
        }