Search code examples
androidwcfsoapksoap2android-ksoap2

Calling WCF Service with KSoap2 with complex objects. WCF receives empty values


I have a WCF service that receive and returns comples objects and I need to call it in Android. I have readed a lot of how to work with Ksoap and at the moment my app is calling the WCF service but WCF service receives empty values in the properties of the request object.

WCF Service

[OperationContract]
WrAsignChkResponse CheckTrsRequest(WrAsignChkModel WrAsignData);

//Request and response objects in .Net
public class WrAsignChkModel
{
    public string Arg1 { get; set; }
    public string Arg2 { get; set; }
    public string Arg3 { get; set; }
    public string Arg4 { get; set; }
}


public class WrAsignChkResponse
{
    public int ResponseCode { get; set; }
    public string Message { get; set; }
    public string RequestStatus { get; set; }
    public string RequestTimeStamp { get; set; }
}

Android Code using KSoap2

private SoapSerializationEnvelope getWebServiceEnvelope(){
    SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
    envelope.dotNet = true;
    envelope.implicitTypes = true;
    return envelope;
}

private void callWebService(){
    SoapObject body = new SoapObject(this.callHeader.getNamespace(), this.callHeader.getMethodName());
    if (this.parameters != null && !this.parameters.isEmpty()) {
        for (PropertyInfo param : this.parameters) {
            body.addProperty(param);
        }
    }
    SoapSerializationEnvelope envelope = this.getWebServiceEnvelope();
    envelope.setOutputSoapObject(body);
    envelope.addMapping(callHeader.getNamespace(), "WrAsignData", WrAsignChkModel.class);

    HttpTransportSE transport = new HttpTransportSE(this.webServiceUrl, this.timeOut);
    transport.debug = true;
    transport.call(callHeader.getSoapAction(), envelope);
    this.soapResponse = envelope.getResponse();
}

Also I have the request Object in Android that implements KVM Serializable.

public class WrAsignChkModel implements KvmSerializable {
    private String Arg1;
    private String Arg2;
    private String Arg3;
    private String Arg4;

    //Getter and setters

    @Override
    public Object getProperty(int index) {
        switch(index)
        {
            case 0:
                return Arg1;
            //...
        }
        return null;
    }

    @Override
    public int getPropertyCount() {
        return 4;
    }

    @Override
    public void setProperty(int index, Object value) {
        switch(index)
        {
            case 0:
                Arg1 = value.toString();
                break;
            //...
            default:
                break;
        }
    }

    @Override
    public void getPropertyInfo(int index, Hashtable properties, PropertyInfo info) {
        switch(index)
        {
            case 0:
                info.type = PropertyInfo.STRING_CLASS;
                info.name = "Arg1";
                break;
            //...
            default:
                break;
        }
    }
}

HttpTransportSE request dump With this request the WCF service is receiving empty values in the WrAsignData object

<v:Envelope xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://www.w3.org/2001/XMLSchema" xmlns:c="http://schemas.xmlsoap.org/soap/encoding/" xmlns:v="http://schemas.xmlsoap.org/soap/envelope/">
<v:Header />
<v:Body>
<CheckTrsRequest xmlns="http://tempuri.org/">
<WrAsignData>
    <Arg1>XXXX</Arg1>
    <Arg2>XXXX</Arg2>
    <Arg3>XXXX</Arg3>
    <Arg4>XXXX</Arg4>
</WrAsignData>
</CheckTrsRequest>
</v:Body>
</v:Envelope>

I have tested the web service with a Windows Forms test application and I have captured the XML that this app is sending with wireshark and I see that it's a difference in the namespace of the parameter.

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
    <CheckTrsRequest xmlns="http://tempuri.org/">
        <WrAsignData xmlns:a="http://schemas.datacontract.org/2004/07/Models.TrsApiModel" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
            <a:Arg1>XXXX</a:Arg1>
            <a:Arg2>XXXX</a:Arg2>
            <a:Arg3>XXXX</a:Arg3>
            <a:Arg4>XXXX</a:Arg4>
        </WrAsignData>
    </CheckTrsRequest>
    </s:Body>
</s:Envelope>

There is no a lot of information on the internet of how to work properly with KSoap2 and I don't find the solution of this. Can anybody help me with this?


Solution

  • I have solved it after hours of investigation. I'll post the answer to help others in the same case.

    Using SoapUI I have seen the namespaces and the XML to send and then trying with KSoap2 generating something similar to that.

    In my class I have added the namespace in the getPropertyInfo method. It is the same namespace to all properties and this will generate an XML with the namespace in all tags. Different than the XML that I have mentioned in my question. But it's ok and is compatible.

    @Override
    public void getPropertyInfo(int index, Hashtable properties, PropertyInfo info) {
        switch(index)
        {
            case 0:
                info.type = PropertyInfo.STRING_CLASS;
                info.name = "Arg1";
                info.namespace = "http://schemas.datacontract.org/2004/07/Models.TrsApiModel"
                break;
            //...
            default:
                break;
        }
    }
    

    Then in my class I have added a method to generate a SoapObject. The namespace is not the same as the properties inside the object.

    public SoapObject getSoapRequest(String nameSpace, String name){
        SoapObject soapRequest = new SoapObject(nameSpace, name);
        for(int i = 0; i < this.getPropertyCount(); i++){
            PropertyInfo prp = new PropertyInfo();
            this.getPropertyInfo(i, null, prp);
            prp.setValue(this.getProperty(i));
            soapRequest.addProperty(prp);
        }
        return soapRequest;
    }
    

    Onse you have the SoapObject to send you have to add int to the Body SoapObject with addSoapObject method.

    SoapObject body = new SoapObject(this.callHeader.getNamespace(), this.callHeader.getMethodName());
    //soapObj is an instance using the getSoapRequest() method mentioned in my piece of code
    body.addSoapObject(soapObj);
    SoapSerializationEnvelope envelope = this.getWebServiceEnvelope();
    envelope.setOutputSoapObject(body);
    

    With that code everything seems to work but not completely. I have experienced that some of the parameters are received well by the server but not all of them. One more tricky thing is that the object properties must be in the same order as the parameters in the SoapUi request. So you have to modify the switches in the methods of the object to have the index in the same order as you can see in the SoapUI XML request.

    Methods to pay attention at with the index to have the same order

    public Object getProperty(int index)
    public void setProperty(int index, Object value)
    public void getPropertyInfo(int index, Hashtable properties, PropertyInfo info)
    

    It takes me a lot of time to discover how WCF is such kind of detailed to receive the correct parameters. So hope my answer will help someone with same problems.