Calling WCF Service with KSoap2 with complex objects. WCF receives empty values
Asked Answered
B

2

0

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?

Baliol answered 12/8, 2020 at 12:8 Comment(0)
B
0

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.

Baliol answered 17/8, 2020 at 8:40 Comment(0)
H
0

Try to use Datacontract and DataMember:

 [DataContract]
    public class WrAsignChkModel
    {
        [DataMember]
        public string Arg1 { get; set; }
        [DataMember]
        public string Arg2 { get; set; }
        [DataMember]
        public string Arg3 { get; set; }
        [DataMember]
        public string Arg4 { get; set; }
    }

    [DataContract]
    public class WrAsignChkResponse
    {
        [DataMember]
        public int ResponseCode { get; set; }
        [DataMember]
        public string Message { get; set; }
        [DataMember]
        public string RequestStatus { get; set; }
        [DataMember]
        public string RequestTimeStamp { get; set; }
    }

Feel free to let me know if the problem persists.

UPDATE

I also think this is a serialization problem. I don’t know how you call the service. If we call it by adding a service reference, the client class should look like the following:

enter image description here

Hedveh answered 13/8, 2020 at 5:50 Comment(10)
Hi! I have added it on the resquest and response object on .Net code and still the same problem. I think that the problem maybe in the KSoap Serialization. Thanks for your help!Baliol
I update my reply,Both client and server classes need to add DataMember.Hedveh
You don't have serialized attributes in the client.Hedveh
Sorry, but I'm not using .Net in the client. I'm calling the service using KSoap2 library for Java in AndroidBaliol
I suggest you generate a proxy class based on the WSDL file of the service to call the WCF service.Hedveh
There are tools in eclipse that can generate proxy classes based on WSDL files.Hedveh
You can create a .net project to call the wcf service to determine whether it is a wcf problem or a KSoap2 problem.Hedveh
Hi, I have a client on Windows forms in .Net to test the service and is working good. The problem is with KSoap serialization and namespaces. I found a similar problem but the solution is not clear for me: #9588077 Ksoap2 documentation is not so good...Baliol
I'm sorry I don't know KSoap2 in particular. Based on the information in the link, I think changing <Arg1> to <a:Arg1> is a solution? I’m not familiar with KSoap2, I don’t know if this solution is feasible.Hedveh
Thanks for trying to helpBaliol
B
0

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.

Baliol answered 17/8, 2020 at 8:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.