Search code examples
javaspring-bootjaxbsoap-clientunmarshalling

Unmarshaller doesn't deserialize properly


I want to consume a SOAP web service from my web application. My web application implemented with Spring framework. The SOAP web service needs authentication, therefore it is expected that username and password information send in SOAP header. I added the security information to the header of sending xml message by overriding doWithMessage. Now I can get the correct response from the SOAP service. But marshal don't deserialize the response to Java object properly.

XML response

<s:Envelope
    xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
    <s:Header>
        <o:Security
            xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" s:mustUnderstand="1">
            <u:Timestamp u:Id="_0">
                <u:Created>2022-02-21T19:00:02.973Z</u:Created>
                <u:Expires>2022-02-21T19:05:02.973Z</u:Expires>
            </u:Timestamp>
        </o:Security>
    </s:Header>
    <s:Body
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <IsEInvoiceUserResponse
            xmlns="http://tempuri.org/">
            <IsEInvoiceUserResult IsSucceded="true" Value="false"/>
        </IsEInvoiceUserResponse>
    </s:Body>
</s:Envelope>

IsEInvoiceUserResponse.java

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "isEInvoiceUserResult"
})
@XmlRootElement(name = "IsEInvoiceUserResponse")
public class IsEInvoiceUserResponse {

@XmlElement(name = "IsEInvoiceUserResult")
protected FlagResponse isEInvoiceUserResult;

/**
 * Gets the value of the isEInvoiceUserResult property.
 *
 * @return
 *     possible object is
 *     {@link FlagResponse }
 *
 */
public FlagResponse getIsEInvoiceUserResult() {
    return isEInvoiceUserResult;
}

/**
 * Sets the value of the isEInvoiceUserResult property.
 *
 * @param value
 *     allowed object is
 *     {@link FlagResponse }
 *
 */
public void setIsEInvoiceUserResult(FlagResponse value) {
    this.isEInvoiceUserResult = value;
}

}

The function in class that extends WebServiceGatewaySupport

public IsEInvoiceUserResponse testUser() {
     IsEInvoiceUser request = new ObjectFactory().createIsEInvoiceUser();
     IsEInvoiceUserResponse response = (IsEInvoiceUserResponse) getWebServiceTemplate().marshalSendAndReceive(request, new SecurityHeader(authentication, ObjectFactory.SOAP_ACTION_IS_EINVOICE_USER));
     return response;
}

response is not null but response's child objects are always null.

After that I have tried to deserialize the xml response string with simple code. But I couldn't achieve with that too. I really don't know where I am doing wrong.

String data = "<s:Envelope\n" +
            "    xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\n" +
            "    xmlns:u=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">\n" +
            "    <s:Header>\n" +
            "        <o:Security s:mustUnderstand=\"1\"\n" +
            "            xmlns:o=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">\n" +
            "            <u:Timestamp u:Id=\"_0\">\n" +
            "                <u:Created>2022-02-18T07:44:56.344Z</u:Created>\n" +
            "                <u:Expires>2022-02-18T07:49:56.344Z</u:Expires>\n" +
            "            </u:Timestamp>\n" +
            "        </o:Security>\n" +
            "    </s:Header>\n" +
            "    <s:Body\n" +
            "        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
            "        xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\n" +
            "        <IsEInvoiceUserResponse\n Value=\"true\" Data=\"The true\"  " +
            "            xmlns=\"http://tempuri.org/\">\n" +
            "            <EInvoiceUserResult IsSucceded=\"true\" Value=\"false\"/>\n" +
            "        </IsEInvoiceUserResponse>\n" +
            "    </s:Body>\n" +
            "</s:Envelope>";

        JAXBContext jc;
        try {
            //SOAPMessage sm = MessageFactory.newInstance().createMessage(null, new ByteArrayInputStream(data.getBytes()));
            jc = JAXBContext.newInstance(IsEInvoiceUserResponse2.class);
            System.out.println("DATA : " + data);
            Unmarshaller unmarshaller = jc.createUnmarshaller();
            JAXBElement<IsEInvoiceUserResponse2> object = unmarshaller.unmarshal(new StringSource(data), IsEInvoiceUserResponse2.class);
            System.out.println("result : " + object.getValue().getValue());

        } catch (JAXBException | SOAPException | IOException e) {
            e.printStackTrace();
        }

Thanks.


Solution

  • I generated Java classes from a WSDL. But I have lots of classes, I don't want to hold unnecessary classes in my project. Therefore I decided to relocate them in other place and I copy when a class is really needed.

    I overlooked that the package info is really important when serializing and deserializing the soap request. So my fault was holding classes in same package. I separate the classes into the packages and I added package-info.java (we can copy the generated one) then the problem is solved.