
本教程旨在解决Android Ksoap2在向.NET Web服务发送包含嵌套整数数组(如`ArrayList`)的自定义对象时遇到的序列化错误。核心解决方案包括将`ArrayList`替换为`Vector`,并为`Vector.class`添加显式Ksoap2类型映射,确保数据正确传输。
在Android开发中,使用Ksoap2库与SOAP协议的Web服务进行交互是常见的需求。然而,当需要发送包含复杂数据类型(尤其是集合类型,如整数数组)的自定义对象到.NET Web服务时,开发者可能会遇到序列化失败的运行时错误,例如java.lang.RuntimeException: Cannot serialize […]。本文将深入探讨这一问题,并提供一个经过验证的解决方案。
问题描述与初步分析
假设我们有一个自定义的KvmSerializable对象,其中包含一个整数列表,例如ArrayList,并尝试将其发送到.NET Web服务。Web服务的XML结构可能如下所示:
int int int string
在Ksoap2的KvmSerializable实现中,我们可能会这样定义filesList:
public class DownloadSelectionInfo implements KvmSerializable { // ... 其他属性 private ArrayList filesList; // ... 构造函数、getter/setter @Override public Object getProperty(int index) { // ... 其他属性处理 switch (index) { case 1: // 假设filesList是第二个属性 return filesList; // ... } return null; } @Override public int getPropertyCount() { return 3; // 示例:Token, Type, DownloadSelectionInfo } @Override public void getPropertyInfo(int index, Hashtable arg1, PropertyInfo info) { // ... 其他属性处理 switch (index) { case 1: info.type = PropertyInfo.VECTOR_CLASS; // 尝试使用VECTOR_CLASS info.name = "filesList"; break; // ... } } @Override public void setProperty(int index, Object value) { // ... 其他属性处理 switch (index) { case 1: // Ksoap2在反序列化时,会为集合中的每个元素调用setProperty // 因此这里需要将value添加到列表中 if (filesList == null) { filesList = new ArrayList(); } filesList.add(Integer.parseInt(value.toString())); break; // ... } }}
在调用Web服务时,我们可能会这样构建SoapObject:
// 构建filesList的SoapObjectSoapObject soFilesList = new SoapObject(Util.getWebService(), "filesList");for (Integer i : downloadSelectionInfo.getFilesList()) { soFilesList.addProperty("int", i); // 为每个整数添加一个名为"int"的属性}soDownloadSelectionInfo.addSoapObject(soFilesList); // 将集合SoapObject添加到父对象// ... 为DownloadSelectionInfo添加PropertyInfoPropertyInfo pi = new PropertyInfo();pi.setName("DownloadSelectionInfo");pi.setValue(downloadSelectionInfo);pi.setType(DownloadSelectionInfo.class);request.addProperty(pi);// 注册自定义类型映射soapEnvelope.addMapping(Util.getWebService(), "DownloadSelectionInfo", DownloadSelectionInfo.class);
尽管我们尝试在getPropertyInfo中将filesList的类型设置为PropertyInfo.VECTOR_CLASS,并且正确地构建了SoapObject,但运行时仍然可能抛出RuntimeException: Cannot serialize [10,9,1]这样的错误。这表明Ksoap2在内部处理ArrayList作为复杂对象属性时存在兼容性问题,尤其是在与.NET Web服务交互时。
Ksoap2集合类型序列化机制
Ksoap2在处理集合类型时,尤其是在向Web服务发送数据时,对Java集合的具体实现类有特定的偏好。ArrayList虽然是Java中最常用的列表实现,但在某些Ksoap2版本和特定Web服务(如.NET)的组合下,其默认序列化行为可能不符合预期。Ksoap2通常对Vector类有更好的内置支持,因为它在Java的早期版本中被广泛用于表示可序列化的动态数组,并且其内部结构与Ksoap2期望的某些序列化模式更为吻合。
当Ksoap2尝试序列化一个KvmSerializable对象中的ArrayList属性时,如果没有显式的、兼容的类型映射,它可能无法正确识别并处理其内部元素,从而导致序列化失败。
解决方案详解
解决此问题的关键在于两点:将ArrayList替换为Vector,并为Vector.class添加显式的Ksoap2类型映射。
1. 使用 Vector 替换 ArrayList
将DownloadSelectionInfo类中的ArrayList改为Vector。Vector是一个线程安全的动态数组,它实现了List接口,并且在Ksoap2的内部处理中通常具有更好的兼容性。
import java.util.Vector; // 导入Vectorpublic class DownloadSelectionInfo implements KvmSerializable { // ... private Vector filesList; // 将ArrayList改为Vector // ...}
2. 更新 KvmSerializable 实现
相应地,getProperty、getPropertyInfo和setProperty方法也需要适配Vector。虽然Vector和ArrayList在方法签名上相似,但关键在于getPropertyInfo中类型声明的准确性。
public class DownloadSelectionInfo implements KvmSerializable { // ... private Vector filesList; // 已更改为Vector // ... 构造函数、getter/setter @Override public Object getProperty(int index) { // ... switch (index) { case 1: return filesList; // ... } return null; } @Override public int getPropertyCount() { return 3; // 示例:Token, Type, DownloadSelectionInfo } @Override public void getPropertyInfo(int index, Hashtable arg1, PropertyInfo info) { // ... switch (index) { case 1: info.type = PropertyInfo.VECTOR_CLASS; // 确保类型为VECTOR_CLASS info.name = "filesList"; break; // ... } } @Override public void setProperty(int index, Object value) { // ... switch (index) { case 1: if (filesList == null) { filesList = new Vector(); // 初始化为Vector } // Ksoap2在反序列化时,会为集合中的每个元素调用setProperty // 因此这里需要将value添加到列表中 filesList.add(Integer.parseInt(value.toString())); break; // ... } }}
3. 添加 Vector 类型的显式映射
这是解决问题的关键一步。即使在getPropertyInfo中声明了VECTOR_CLASS,Ksoap2也可能需要一个全局的类型映射来正确识别和处理Vector对象。我们需要在SoapEnvelope中为Vector.class添加一个显式映射。
// ... 在构建SoapEnvelope之后,但在执行请求之前// 注册自定义类型映射soapEnvelope.addMapping(Util.getWebService(), "DownloadSelectionInfo", DownloadSelectionInfo.class);// 关键:为filesList的实际类型(Vector)添加映射soapEnvelope.addMapping(Util.getWebService(), "filesList", Vector.class); // 这里的"filesList"是XML中的元素名
这里的”filesList”应与XML中表示集合的元素名称相匹配。如果Web服务期望的是一个通用的数组类型,有时可能需要映射到Array.class或SoapObject.class,但对于嵌套在自定义对象中的Vector,直接映射Vector.class通常是有效的。
4. 构建 SoapObject 包含集合
构建SoapObject的部分保持不变,因为它已经正确地为每个整数添加了名为”int”的属性,符合XML结构。
// 构建filesList的SoapObjectSoapObject soFilesList = new SoapObject(Util.getWebService(), "filesList");for (Integer i : downloadSelectionInfo.getFilesList()) { // getFilesList()现在返回Vector soFilesList.addProperty("int", i);}soDownloadSelectionInfo.addSoapObject(soFilesList);
完整示例代码
结合上述所有更改,以下是修正后的DownloadSelectionInfo类和Web服务调用代码片段:
DownloadSelectionInfo 类:
import org.ksoap2.serialization.KvmSerializable;import org.ksoap2.serialization.PropertyInfo;import org.ksoap2.serialization.SoapObject;import java.util.Hashtable;import java.util.Vector; // 导入Vectorpublic class DownloadSelectionInfo implements KvmSerializable { private String token; private String type; private Vector filesList; // 更改为Vector private String filename; // 默认构造函数 public DownloadSelectionInfo() { this.filesList = new Vector(); } // 带参数的构造函数(可选) public DownloadSelectionInfo(String token, String type, Vector filesList, String filename) { this.token = token; this.type = type; this.filesList = filesList; this.filename = filename; } // Getter和Setter方法 public String getToken() { return token; } public void setToken(String token) { this.token = token; } public String getType() { return type; } public void setType(String type) { this.type = type; } public Vector getFilesList() { return filesList; } public void setFilesList(Vector filesList) { this.filesList = filesList; } public String getFilename() { return filename; } public void setFilename(String filename) { this.filename = filename; } @Override public Object getProperty(int index) { switch (index) { case 0: return token; case 1: return type; case 2: return filesList; // 返回Vector对象 case 3: return filename; } return null; } @Override public int getPropertyCount() { return 4; // token, type, filesList, filename } @Override public void getPropertyInfo(int index, Hashtable arg1, PropertyInfo info) { switch (index) { case 0: info.type = PropertyInfo.STRING_CLASS; info.name = "Token"; break; case 1: info.type = PropertyInfo.STRING_CLASS; info.name = "Type"; break; case 2: info.type = PropertyInfo.VECTOR_CLASS; // 声明为VECTOR_CLASS info.name = "filesList"; break; case 3: info.type = PropertyInfo.STRING_CLASS; info.name = "Filename"; break; default: break; } } @Override public void setProperty(int index, Object value) { switch (index) { case 0: token = value.toString(); break; case 1: type = value.toString(); break; case 2: // Ksoap2在反序列化时,会为集合中的每个元素调用setProperty // 因此这里需要将value添加到列表中 if (filesList == null) { filesList = new Vector(); } filesList.add(Integer.parseInt(value.toString())); break; case 3: filename = value.toString(); break; default: break; } }}
Web服务调用代码片段:
import org.ksoap2.SoapEnvelope;import org.ksoap2.serialization.PropertyInfo;import org.ksoap2.serialization.SoapObject;import org.ksoap2.serialization.SoapSerializationEnvelope;import org.ksoap2.transport.HttpTransportSE;import java.util.Vector; // 导入Vectorpublic class WebServiceCaller { // 假设Util.getWebService()返回Web服务的命名空间 // 假设Util.getWebServiceGed()返回Web服务的命名空间 (根据原答案修正,可能是一个特定的命名空间) private static final String NAMESPACE = Util.getWebService(); private static final String URL = "YOUR_WEB_SERVICE_URL"; // 替换为实际URL private static final String SOAP_ACTION = NAMESPACE + "YOUR_METHOD_NAME"; // 替换为实际SOAP Action public void callWebService(DownloadSelectionInfo downloadSelectionInfo) { SoapObject request = new SoapObject(NAMESPACE, "YourMethodName"); // 替换为实际方法名 // 为DownloadSelectionInfo构建SoapObject (如果DownloadSelectionInfo本身是方法的参数) SoapObject soDownloadSelectionInfo = new SoapObject(NAMESPACE, "DownloadSelectionInfo"); // 添加Token和Type属性 soDownloadSelectionInfo.addProperty("Token", downloadSelectionInfo.getToken()); soDownloadSelectionInfo.addProperty("Type", downloadSelectionInfo.getType()); // 添加filesList SoapObject soFilesList = new SoapObject(NAMESPACE, "filesList"); // XML中的filesList元素名 for (Integer i : downloadSelectionInfo.getFilesList()) { soFilesList.addProperty("int", i); // XML中的元素 } soDownloadSelectionInfo.addSoapObject(soFilesList); // 添加Filename属性 soDownloadSelectionInfo.addProperty("Filename", downloadSelectionInfo.getFilename()); // 将构建好的DownloadSelectionInfo SoapObject作为参数添加到请求中 PropertyInfo piDownloadSelectionInfo = new PropertyInfo(); piDownloadSelectionInfo.setName("DownloadSelectionInfo"); // 参数名 piDownloadSelectionInfo.setValue(soDownloadSelectionInfo); // 值是一个SoapObject piDownloadSelectionInfo.setType(soDownloadSelectionInfo.getClass()); // 类型是SoapObject request.addProperty(piDownloadSelectionInfo); // 或者,如果DownloadSelectionInfo直接作为方法参数,并且是KvmSerializable // PropertyInfo pi = new PropertyInfo(); // pi.setName("DownloadSelectionInfo"); // 参数名 // pi.setValue(downloadSelectionInfo); // 值是KvmSerializable对象 // pi.setType(DownloadSelectionInfo.class); // 类型是KvmSerializable类 // request.addProperty(pi); SoapSerializationEnvelope soapEnvelope = new SoapSerializationEnvelope(SoapEnvelope.VER11); soapEnvelope.dotNet = true; // 针对.NET Web服务设置 soapEnvelope.setOutputSoapObject(request); // 注册自定义类型映射 soapEnvelope.addMapping(NAMESPACE, "DownloadSelectionInfo", DownloadSelectionInfo.class); // 关键:为filesList的实际类型(Vector)添加映射 // 根据原答案,这里可能需要使用另一个命名空间,例如 Util.getWebServiceGed() soapEnvelope.addMapping(Util.getWebServiceGed(), "filesList", Vector.class); HttpTransportSE androidHttpTransport = new HttpTransportSE(URL); try { androidHttpTransport.call(SOAP_ACTION, soapEnvelope); // 处理响应 SoapObject result = (SoapObject) soapEnvelope.getResponse(); // ... } catch (Exception e) { e.printStackTrace(); // 错误处理 } }}
注意:在soapEnvelope.addMapping(Util.getWebServiceGed(), “filesList”, Vector.class);这一行中,Util.getWebServiceGed()是根据原始解决方案提供的,可能代表Web服务中用于filesList元素的特定命名空间,或者是一个通用的命名空间。在实际应用中,您需要根据Web服务的WSDL文件确认正确的命名空间。
总结与最佳实践
当使用Android Ksoap2与.NET Web服务进行复杂数据类型(特别是包含集合的自定义对象)交互时,请遵循以下最佳实践:
优先使用 Vector 替代 ArrayList: 在KvmSerializable实现中,对于列表或数组类型的属性,Vector通常比ArrayList具有更好的Ksoap2兼容性。准确声明 PropertyInfo.type: 在getPropertyInfo方法中,确保为Vector类型的属性设置info.type = PropertyInfo.VECTOR_CLASS。添加显式类型映射: 这是最关键的一步。除了为自定义KvmSerializable类添加映射外,务必为Vector.class也添加一个显式的soapEnvelope.addMapping(),并确保命名空间和元素名与Web服务WSDL定义相符。.NET兼容性设置: 对于.NET Web服务,务必设置soapEnvelope.dotNet = true;。WSDL分析: 仔细分析Web服务的WSDL文件,了解其期望的XML结构、元素名称和命名空间,这对于正确构建SoapObject和添加类型映射至关重要。
通过遵循这些步骤,您可以有效地解决Ksoap2在序列化嵌套集合到.NET Web服务时遇到的运行时错误,确保数据传输的顺利进行。
以上就是Android Ksoap2序列化嵌套整数数组到.NET Web服务的解决方案的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/205036.html
微信扫一扫
支付宝扫一扫