Xamarin跨平台应用Android端调用WebService返回DataTable触发序列化异常
问题原因及解决方案
核心原因
这个错误本质是Xamarin Android(基于Mono运行时)与Windows .NET Framework在WebService代理类反射、DataTable类型处理上的兼容性差异:
- .NET Framework对DataTable的序列化/反序列化支持完善,但Mono对DataTable的反射机制支持有限,自动生成的WebService代理类在处理返回DataTable的方法时,反射
GetSeccoesResponseGetSeccoesResult类型会失败。 - 自动生成的代理类中,部分内部类型的访问权限在Android平台下无法被正常反射,而Windows环境对此限制更宽松。
具体解决方案
1. 替换WebService返回类型(最优解)
修改WebService的GetSeccoes方法,不再返回DataTable,改为返回自定义实体类列表(比如List<SeccaoCentro>):
- 服务端定义与客户端一致的
SeccaoCentro类,将数据库查询结果映射为实体列表后返回。 - 重新生成客户端WebService代理,此时代理类会生成强类型返回对象,完全绕过DataTable的跨平台兼容性问题。
2. 绕过自动代理类,手动调用WebService
如果无法修改服务端,直接跳过自动生成的wsProducao代理,用Http请求手动调用并解析XML:
async void CarregaSeccoes() { List<SeccaoCentro> Seccoes = new List<SeccaoCentro>(); var soapRequest = @"<?xml version=""1.0"" encoding=""utf-8""?> <soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/""> <soap:Body> <GetSeccoes xmlns=""你的WebService命名空间""> <TipoPlataforma>" + Application.Current.Properties["TipoPlataforma"] + @"</TipoPlataforma> <Empresa>" + Application.Current.Properties["Empresa"] + @"</Empresa> <Utilizador>" + Application.Current.Properties["Utilizador"] + @"</Utilizador> <Password>" + Application.Current.Properties["Password"] + @"</Password> </GetSeccoes> </soap:Body> </soap:Envelope>"; var request = (HttpWebRequest)WebRequest.Create("你的WebService地址"); request.Method = "POST"; request.ContentType = "text/xml; charset=utf-8"; request.Headers.Add("SOAPAction", "你的WebService命名空间/GetSeccoes"); using (var streamWriter = new StreamWriter(request.GetRequestStream())) { streamWriter.Write(soapRequest); } using (var response = (HttpWebResponse)await request.GetResponseAsync()) using (var streamReader = new StreamReader(response.GetResponseStream())) { var xmlResponse = streamReader.ReadToEnd(); XmlDocument xml = new XmlDocument(); xml.LoadXml(xmlResponse); // 注意调整XPath,匹配实际返回的XML结构 XmlNodeList xNodeList = xml.SelectNodes("//GetSeccoesResult/DocumentElement/Seccoes"); foreach (XmlNode xNode in xNodeList) { Seccoes.Add(new SeccaoCentro() { IDSeccao = Convert.ToInt32(xNode["IDSeccao"].InnerText), Seccao = xNode["Seccao"].InnerText, Descricao = xNode["Seccao"].InnerText.ToUpper() + " - " + xNode["Descricao"].InnerText.ToUpper() }); } } cvSeccoes.ItemsSource = Seccoes.Count > 0 ? Seccoes : null; }
3. 调整代理类的访问权限(临时方案)
如果坚持使用自动生成的代理类:
- 找到生成的
Producao.cs代理文件,检查GetSeccoesResponseGetSeccoesResult类型的访问修饰符,确保它是public的。 - 若该类型为
internal,修改为public——Android平台的反射机制对非public类型的访问限制更严格。
内容的提问来源于stack exchange,提问作者Bruno Gomes
相关产品推荐
相关产品推荐

