使用具有复杂对象的 KSoap2 调用 WCF 服务。 WCF 接收空值

Posted

技术标签:

【中文标题】使用具有复杂对象的 KSoap2 调用 WCF 服务。 WCF 接收空值【英文标题】:Calling WCF Service with KSoap2 with complex objects. WCF receives empty values 【发布时间】:2020-12-02 04:24:49 【问题描述】:

我有一个接收和返回复杂对象的 WCF 服务,我需要在 android 中调用它。我已经阅读了很多如何使用 Ksoap 的内容,目前我的应用正在调用 WCF 服务,但 WCF 服务在请求对象的属性中接收到空值。

WCF 服务

[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; 

使用 KSoap2 的 Android 代码

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();

我在 Android 中也有实现 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 请求转储 通过此请求,WCF 服务将在 WrAsignData 对象中接收空值

<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>

我已经使用 Windows 窗体测试应用程序测试了 Web 服务,并且我已经捕获了该应用程序使用 wireshark 发送的 XML,我发现这是参数命名空间的差异。

<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>

互联网上没有很多关于如何正确使用 KSoap2 的信息,我也没有找到解决方案。有人可以帮我解决这个问题吗?

【问题讨论】:

【参考方案1】:

尝试使用 Datacontract 和 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; 
    

如果问题仍然存在,请随时告诉我。

更新

我也认为这是一个序列化问题。我不知道你怎么称呼这项服务。如果我们通过添加服务引用来调用它,客户端类应该如下所示:

【讨论】:

嗨!我已将它添加到 .Net 代码的 resquest 和 response 对象上,但仍然是同样的问题。我认为问题可能出在 KSoap 序列化中。感谢您的帮助! 我更新我的回复,客户端和服务端类都需要添加DataMember。 您在客户端没有序列化属性。 抱歉,我没有在客户端使用 .Net。我正在使用 Android 中 Java 的 KSoap2 库调用该服务 建议你根据服务的WSDL文件生成一个代理类来调用WCF服务。【参考方案2】:

经过数小时的调查,我已经解决了这个问题。我会发布答案以帮助遇到同样情况的其他人。

使用 SoapUI,我看到了要发送的命名空间和 XML,然后尝试使用 KSoap2 生成类似的东西。

在我的课程中,我在 getPropertyInfo 方法中添加了命名空间。它是所有属性的相同命名空间,这将生成一个包含所有标签中命名空间的 XML。与我在问题中提到的 XML 不同。但是没关系,而且兼容。

@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;
    

然后在我的班级中,我添加了一个生成 SoapObject 的方法。命名空间与对象内部的属性不同。

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;

如果您有要发送的 SoapObject,则必须使用 addSoapObject 方法将 int 添加到 Body SoapObject。

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);

使用该代码,一切似乎都可以正常工作,但并非完全正常。我经历过一些参数被服务器很好地接收,但不是全部。另一件棘手的事情是对象属性必须与 SoapUi 请求中的参数的顺序相同。因此,您必须修改对象方法中的开关,以使索引的顺序与您在 SoapUI XML 请求中看到的顺序相同。

注意与索引顺序一致的方法

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

我花了很多时间才发现 WCF 是如何如此详细地接收正确的参数。所以希望我的回答能帮助遇到同样问题的人。

【讨论】:

以上是关于使用具有复杂对象的 KSoap2 调用 WCF 服务。 WCF 接收空值的主要内容,如果未能解决你的问题,请参考以下文章

Ksoap2 Android - 如何为复杂对象的子属性指定命名空间?

使用 ksoap2 使用 WCF 服务

使用 KSOAP2 从 Android 向 WCF 发送数据

WCF 服务调用具有复杂数据类型的非托管 c++ dll

将复杂对象传递到 WCF Rest 服务

一步一步搭建客服系统 客户列表 - JS($.ajax)调用WCF 遇到的各种坑