【问题标题】:Error deserializing parameter in WCF service from Android kSOAP从 Android kSOAP 反序列化 WCF 服务中的参数时出错
【发布时间】:2011-08-02 03:48:07
【问题描述】:

8/5/11:在帖子底部附近更新

我正在尝试使用 kSOAP 从我的 Android 应用程序中调用我的 WCF Web 服务方法。该方法采用 4 个参数。其中 3 个是 Guid 类型(编组 Java 的 UUID 工作),最后一个是自定义类型:LocationLibrary.Location。这种类型位于一个单独的 DLL (LocationLibrary) 中,我将其加载到 WCF Web 服务项目中,它基本上由两个双精度值组成,纬度和经度。

    [OperationContract]       
    byte[] GetData(Guid deviceId, Guid appId, Guid devKey, LocationLibrary.Location loc);

项目LocationLibrary中的Location类很简单:

    namespace LocationLibrary
    {
    public class Location
    {
        public double latitude { get; set; }

        public double longitude { get; set; }

        public new string ToString()
        {
            return latitude.ToString() + "," + longitude.ToString();
        }

        public bool Equals(Location loc)
        {
            return this.latitude == loc.latitude && this.longitude == loc.longitude;
        }
    }
}

在我的 Android 项目中,我创建了一个类似于 .NET 版本的名为“Location”的类:

    public class Location {
    public double latitude;
    public double longitude;

    public Location() {}

    public static Location fromString(String s)
    {
    //Format from SOAP message is "anyType{latitude=39.6572799682617; longitude=-78.9278602600098; }"
    Location result = new Location();
    String[] tokens = s.split("=");
    String lat = tokens[1].split(";")[0];
    String lng = tokens[2].split(";")[0];
    result.latitude = Double.parseDouble(lat);
    result.longitude = Double.parseDouble(lng); 
    return result;
    }

    public String toString()
    {       
    return Double.toString(latitude) + "," + Double.toString(longitude);    
    }
}

使用 kSOAP 连接到 Web 服务时,我执行以下操作:

    private final String SOAP_ACTION = "http://tempuri.org/IMagicSauceV3/GetData";
    private final String OPERATION_NAME = "GetData";     
    private final String WSDL_TARGET_NAMESPACE = "http://tempuri.org/";
    private final String SOAP_ADDRESS = "http://mydomain.com/myservice.svc";
    private final SoapSerializationEnvelope envelope;

    SoapObject request = new SoapObject(WSDL_TARGET_NAMESPACE, OPERATION_NAME);
    request.addProperty(Cloud8Connector.deviceIdProperty);
    request.addProperty(Cloud8Connector.appIdProperty);
    request.addProperty(Cloud8Connector.devKeyProperty);        
    PropertyInfo locationProperty = new PropertyInfo();
    locationProperty.setName("loc");
    locationProperty.setValue(Cloud8Connector.myLoc);
    locationProperty.setType(Location.class);
    request.addProperty(locationProperty);

    envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
    envelope.dotNet = true;
       envelope.setOutputSoapObject(request);
       MarshalUUID mu = new MarshalUUID();
    mu.register(envelope);
    MarshalLocation ml = new MarshalLocation();
    ml.register(envelope);

     byte[] result = null;
    HttpTransportSE httpRequest = new HttpTransportSE(SOAP_ADDRESS);

    try
    {
        httpRequest.call(SOAP_ACTION, envelope);

        String payloadString = ((SoapPrimitive)(envelope.getResponse())).toString();
        result = Base64.decode(payloadString, Base64.DEFAULT);         
    }
    catch(Exception e)
    {
        e.printStackTrace();
    }

如您所见,我为 Location 创建了一个简单的 Marshal 类,它基本上只使用 fromString 和 toString 方法。我将信封注册到位置的 marshal 实例:

    MarshalLocation ml = new MarshalLocation();
    ml.register(envelope);

这是 Location 的元帅类:

    public class MarshalLocation implements Marshal
    {

    public Object readInstance(XmlPullParser parser, String namespace, String name, 
            PropertyInfo expected) throws IOException, XmlPullParserException {
        return Location.fromString(parser.nextText());                                
    }

    public void register(SoapSerializationEnvelope cm) {
         cm.addMapping(cm.xsd, "Location", Location.class, this);

    }

    public void writeInstance(XmlSerializer writer, Object obj) throws IOException {

            writer.text(((Location)obj).toString());             
        }    
    }

但是,我收到了从 WCF 返回的此错误,并且似乎无法使其正常工作。从错误和搜索中,我认为我需要对我的网络服务进行一些调整,但我不确定解决这个问题的最佳方法到底是什么。

我已尝试将其添加到 Web 服务的 web.config 文件中:

        <system.runtime.serialization>
    <dataContractSerializer>
        <declaredTypes>
            <add type="Location, LocationLibrary, Version=1.0.0.0,Culture=neutral,PublicKeyToken=null">
                <knownType type="Location, LocationLibrary, Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"></knownType>
            </add>
        </declaredTypes>
    </dataContractSerializer>
</system.runtime.serialization>

我已经尝试将 ServiceKnownType 属性添加到接口方法签名中:

    [OperationContract]       
    [ServiceKnownType(typeof(LocationLibrary.Location))]
    byte[] GetData(Guid deviceId, Guid appId, Guid devKey, LocationLibrary.Location loc);

但我仍然收到此错误:(

你能指出我正确的方向吗?谢谢!

错误:

07-30 00:42:08.186:WARN/System.err(8723):SoapFault - 故障代码: 'a:DeserializationFailed' faultstring: '格式化程序抛出了一个 尝试反序列化消息时出现异常:出现错误 在尝试反序列化参数http://tempuri.org/:loc 时。这 InnerException 消息是“第 1 行位置 522 中的错误。元素 'http://tempuri.org/:loc' 包含来自映射到 名称“http://www.w3.org/2001/XMLSchema:Location”。解串器有 不知道映射到此名称的任何类型。考虑使用一个 DataContractResolver 或将 'Location' 对应的类型添加到 已知类型的列表 - 例如,通过使用 KnownTypeAttribute 属性或通过将其添加到传递给的已知类型列表中 DataContractSerializer.'。有关更多详细信息,请参阅 InnerException。 faultactor:'null' 详细信息:org.kxml2.kdom.Node@4053aa28

更新

理想情况下,我不必将 LocationLibrary 类移动到主 WCF 项目中,因为这会使处理期望它位于不同命名空间中的旧客户端变得复杂。但是,我确实通过将这些类移动到主 WCF 项目中并将 java 中的 marshal 类修改为:

    public void register(SoapSerializationEnvelope cm) {            
         cm.addMapping("http://schemas.datacontract.org/2004/07/MagicSauceV3", "Location", Location.class, this);
    }        

    public void writeInstance(XmlSerializer writer, Object obj) throws IOException {
            Location loc = (Location)obj;
            writer.startTag("http://schemas.datacontract.org/2004/07/MagicSauceV3", "latitude");
            writer.text(Double.toString(loc.latitude));
            writer.endTag("http://schemas.datacontract.org/2004/07/MagicSauceV3", "latitude");
            writer.startTag("http://schemas.datacontract.org/2004/07/MagicSauceV3", "longitude");
            writer.text(Double.toString(loc.longitude));
            writer.endTag("http://schemas.datacontract.org/2004/07/MagicSauceV3", "longitude");       
        }

现在我让它工作了,我只是让它与 LocationLibrary WCF 类一起工作(在一个单独的 DLL 中)。所以我修改了元帅类:

    public void register(SoapSerializationEnvelope cm) {            
         cm.addMapping("http://schemas.datacontract.org/2004/07/LocationLibrary", "Location", Location.class, this);
    }


    public void writeInstance(XmlSerializer writer, Object obj) throws IOException {
            Location loc = (Location)obj;
            writer.startTag("http://schemas.datacontract.org/2004/07/LocationLibrary", "latitude");
            writer.text(Double.toString(loc.latitude));
            writer.endTag("http://schemas.datacontract.org/2004/07/LocationLibrary", "latitude");
            writer.startTag("http://schemas.datacontract.org/2004/07/LocationLibrary", "longitude");
            writer.text(Double.toString(loc.longitude));
            writer.endTag("http://schemas.datacontract.org/2004/07/LocationLibrary", "longitude");       
        }

这似乎应该成功,因为它生成的 XML 与确实有效的 WP7 版本相似。

WP7 工作 XML 请求:

<GetData xmlns="http://tempuri.org/">
<deviceId>{valid GUID}</deviceId>
<appId>{valid GUID}</appId>
<devKey>{valid GUID}</devKey>
<loc xmlns:d4p1="http://schemas.datacontract.org/2004/07/LocationLibrary" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<d4p1:latitude>47.65</d4p1:latitude>
<d4p1:longitude>-122.34</d4p1:longitude>
</loc>
</GetData>

Android 非工作 XML 请求:

<GetData xmlns="http://tempuri.org/" id="o0" c:root="1">
<deviceId i:type="d:UUID">{valid GUID}</deviceId>
<appId i:type="d:UUID">{valid GUID}</appId>
<devKey i:type="d:UUID">{valid GUID}</devKey>
<loc i:type="n0:Location" xmlns:n0="http://schemas.datacontract.org/2004/07/LocationLibrary">
<n0:latitude>47.65</n0:latitude>
<n0:longitude>-122.34</n0:longitude>
</loc>
</GetData>

上面的 Android XML 请求产生了这个响应错误:

08-05 22:51:23.703: WARN/System.err(1382): SoapFault - faultcode: 'a:DeserializationFailed' faultstring: '格式化程序在尝试反序列化消息时抛出异常:出现错误同时试图反序列化参数http://tempuri.org/:loc。 InnerException 消息是“第 1 行位置 590 中的错误。元素“http://tempuri.org/:loc”包含映射到名称“http://schemas.datacontract.org/2004/07/”的类型的数据位置库:位置'。反序列化器不知道映射到此名称的任何类型。考虑使用 DataContractResolver 或将与 'Location' 对应的类型添加到已知类型列表中 - 例如,通过使用 KnownTypeAttribute 属性或将其添加到传递给 DataContractSerializer 的已知类型列表中。有关更多详细信息,请参阅 InnerException。 faultactor:'null' 详细信息:org.kxml2.kdom.Node@4054bcb8

我看到的唯一显着区别是工作 XML 请求中的“loc”属性: xmlns:i="http://www.w3.org/2001/XMLSchema-instance"

我不知道这是否会有所作为(而且它似乎没有被嵌套标签引用)。我也不知道如何添加额外的命名空间而不弄乱另一个命名空间。

很抱歉,这篇文章很长,但我想确保您拥有所有需要帮助的信息 :)

谢谢!

【问题讨论】:

  • 显示您的MarshalLocation 课程。
  • 好的,现在添加。谢谢!

标签: android wcf soap deserialization ksoap2


【解决方案1】:

正如我所料,您只是在做ToString,但这是错误的。您的位置不会像两个连接值的字符串那样传输。它作为序列化为 XML 的对象传输。比如:

<Location> <!-- or loc -->
   <latitude>10.0</latitude>
   <longitude>-20.0</longitude>
</Location>

【讨论】:

  • 哦,有道理,谢谢!那么我应该为刚刚创建这些标签的 java Location 类创建一个 toXml() 方法,然后从 marshal 类中调用它吗?
  • 是的,试试吧。我对 kSoap 知之甚少,所以我不确定您是否必须同时执行包装器元素。你将不得不玩它一点点。您可以使用WCF message logging 查看您发送到 WCF 服务的内容(并且您可以构建非常简单的 .NET 客户端来将其消息与您的进行比较)。
  • 谢谢!它在我最初的尝试中不起作用,所以我会按照你的建议查看消息记录。之后我会跟进。再次感谢!
  • 更新:到目前为止,当我将 WCF 中的 Location 类移动到主项目而不是单独的 DLL 时,我才让它工作。然后,我更改了 java marshal 类中 writeInstance 方法的工作方式,以使用 XMLSerializer.startTag 和 .endTag 而不是 .text 方法。一旦我让它与单独的 DLL 中的 LocationLibrary 一起工作,我将发布该解决方案所需的所有更改。由于野外客户端指向当前版本,因此像现在这样更改服务会很痛苦。
  • 我已经用更多信息更新了原始问题。感谢 Ladislav 迄今为止的帮助!
【解决方案2】:

感谢 Ladislav 指出正确的方向!我不确定这里有什么礼节来奖励这一点,但如果有人能提出建议,我会很乐意效仿。

答案是双重的,我可以确认它现在可以在单独的 DLL 中与 LocationLibrary 的 Location 类一起使用:

  1. 使用我的编组类更改我在 Android 中形成 XML 请求的方式。 writeInstance 方法仅使用 XmlSerializer 类的 .textMethod,在 Location 类上调用 .toString()。我对其进行了更改以利用形成 XML 标记的正确方法(.startTag 和 .endTag 方法):

    public void writeInstance(XmlSerializer writer, Object obj) throws IOException {
        Location loc = (Location)obj;
        writer.startTag("http://schemas.datacontract.org/2004/07/LocationLibrary", "latitude");
        writer.text(Double.toString(loc.latitude));
        writer.endTag("http://schemas.datacontract.org/2004/07/LocationLibrary", "latitude");
        writer.startTag("http://schemas.datacontract.org/2004/07/LocationLibrary", "longitude");
        writer.text(Double.toString(loc.longitude));
        writer.endTag("http://schemas.datacontract.org/2004/07/LocationLibrary", "longitude");       
    }
    
  2. 向 WCF 服务添加一些简单的标签。我将 [DataContract] 添加到 WCF 服务使用的 LocationLibrary 程序集中的 Location 类中,并将 [DataMember] 添加到其每个成员中:

    [DataContract]
    public class Location
    {
    [DataMember]
    public double latitude { get; set; }
    
    [DataMember]
    public double longitude { get; set; }
    
    public new string ToString()
    {
        return latitude.ToString() + "," + longitude.ToString();
    }
    
    public bool Equals(Location loc)
    {
        return this.latitude == loc.latitude && this.longitude == loc.longitude;
    }
    

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多