Android Ksoap2序列化嵌套整数数组到.NET Web服务的解决方案

android ksoap2序列化嵌套整数数组到.net web服务的解决方案

本教程旨在解决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

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Bose SoundLink Flex评测:有小体积,也有好音质
上一篇 2025年11月1日 23:24:08
AI推文助手如何制作产品教程 AI推文助手的教学内容创作
下一篇 2025年11月1日 23:24:10

相关推荐

  • 修复Django电商项目中AJAX过滤产品列表图片不显示问题

    在Django电商项目中,当使用AJAX动态加载过滤后的产品列表时,常遇到图片无法正常显示的问题。这通常是由于前端模板中图片加载方式(如data-setbg属性结合JavaScript库)与AJAX动态内容更新机制不兼容所致。解决方案是直接在AJAX返回的HTML中使用标准的标签来渲染图片,确保浏览…

    2026年5月10日
    000
  • Matplotlib 地图中多类型图例的创建与优化

    Matplotlib 地图中多类型图例的创建与优化Matplotlib 地图中多类型图例的创建与优化Matplotlib 地图中多类型图例的创建与优化Matplotlib 地图中多类型图例的创建与优化

    本教程旨在解决matplotlib地图可视化中,如何在一个图例中同时展示颜色块(如区域分类)和自定义标记(如特定兴趣点)的问题。文章详细介绍了当传统`patch`对象无法正确显示标记时,如何利用`matplotlib.lines.line2d`创建标记图例句柄,并将其与颜色块图例句柄合并,从而生成一…

    2026年5月10日 用户投稿
    100
  • 比特币新手教程 比特币交易平台有哪些

    比特币是一种去中心化的数字货币,基于区块链技术实现点对点交易,具有匿名性、有限发行和不可篡改等特点;新手可通过交易所购买,P2P交易获得比特币,常用平台包括Binance、OKX和Huobi;交易流程包括注册账户、实名认证、绑定支付方式、充值法币并下单购买,可选择市价单或限价单;比特币存储方式有交易…

    2026年5月10日
    000
  • 修复点击时按钮抖动:CSS垂直对齐实践

    本文探讨了在Web开发中,交互式按钮(如播放/暂停按钮)在点击时发生意外垂直位移的问题。通过分析CSS样式变化对元素布局的影响,我们发现这是由于按钮不同状态下的边框样式和内边距改变,以及默认的垂直对齐行为共同作用所致。核心解决方案是利用CSS的vertical-align属性,将其设置为middle…

    2026年5月10日
    000
  • 使用 Jupyter Notebook 进行探索性数据分析

    Jupyter Notebook通过单元格实现代码与Markdown结合,支持数据导入(pandas)、清洗(fillna)、探索(matplotlib/seaborn可视化)、统计分析(describe/corr)和特征工程,便于记录与分享分析过程。 Jupyter Notebook 是进行探索性…

    2026年5月10日
    000
  • 如何在HTML中插入表单元素_HTML表单控件与输入类型使用指南

    HTML表单通过标签构建,包含action和method属性定义数据提交目标与方式,常用input类型如text、password、email等适配不同输入需求,配合label、required、placeholder提升可用性,结合textarea、select、button等控件实现完整交互,是…

    2026年5月10日
    000
  • 前端缓存策略与JavaScript存储管理

    根据数据特性选择合适的存储方式并制定清晰的读写与清理逻辑,能显著提升前端性能;合理运用Cookie、localStorage、sessionStorage、IndexedDB及Cache API,结合缓存策略与定期清理机制,可在保证用户体验的同时避免安全与性能隐患。 前端缓存和JavaScript存…

    2026年5月10日
    100
  • HTML5网页如何实现手势操作 HTML5网页移动端交互的处理技巧

    首先利用原生touch事件实现滑动判断,再通过preventDefault解决滚动冲突,接着引入Hammer.js处理复杂手势,最后通过优化点击区域、避免事件冲突和增加视觉反馈提升体验。 在移动端浏览器中,HTML5网页可以通过触摸事件实现手势操作,提升用户体验。虽然原生JavaScript提供了基…

    2026年5月10日
    000
  • 深入理解 Express.js 中 next() 参数的作用与中间件机制

    本文深入探讨 express.js 中间件函数中的 `next()` 参数。它负责将控制权传递给请求-响应周期中的下一个中间件或路由处理程序。文章将详细解释 `next()` 的工作原理、中间件的注册与执行顺序,以及不正确使用 `next()` 可能导致请求挂起的风险,并通过代码示例和实际应用场景,…

    2026年5月10日
    000
  • JavaScript 闭包:理解闭包原理与内存泄漏问题

    闭包是函数访问其外部作用域变量的能力,即使外部函数已执行完毕。如 inner 函数引用 outer 中的 count,形成闭包,使变量持久存在。闭包本身无害,但可能因延长变量生命周期导致内存泄漏,例如事件监听器引用大对象时。若未及时清理 DOM 事件或定时器,闭包会阻止垃圾回收,造成内存占用过高。解…

    2026年5月10日
    000
  • JavaScript 动态菜单点击高亮效果实现教程

    本教程详细介绍了如何使用 JavaScript 实现动态菜单的点击高亮功能。通过事件委托和状态管理,当用户点击菜单项时,被点击项会高亮显示(绿色),同时其他菜单项恢复默认样式(白色)。这种方法避免了不必要的DOM操作,提高了性能和代码可维护性,确保了无论点击方向如何,功能都能稳定运行。 动态菜单高亮…

    2026年5月10日
    200
  • JavaScript函数中插入加载动画(Spinner)的正确方法

    本文旨在解决在JavaScript函数中插入加载动画(Spinner)时遇到的异步问题。通过引入async/await和Promise.all,确保在数据处理完成前后正确显示和隐藏加载动画,提升用户体验。我们将提供两种实现方案,并详细解释其原理和优势。 在Web开发中,当执行耗时操作时,显示加载动画…

    2026年5月10日
    000
  • Golang空接口如何应用在项目中

    空接口可用于接收任意类型值,常见于日志函数、通用数据结构、JSON动态解析及配置驱动逻辑,提升代码灵活性,但需配合类型断言确保安全,避免滥用以降低维护成本。 空接口 interface{} 在 Go 语言中是一个非常灵活的类型,它可以存储任何类型的值。虽然它牺牲了一部分类型安全,但在实际项目中合理使…

    2026年5月10日
    100
  • 动态更新圆形进度条:JavaScript成绩计算器集成指南

    本文档旨在指导开发者如何将JavaScript成绩计算系统与动态圆形进度条集成,实现可视化展示平均成绩。我们将详细讲解如何修改现有的JavaScript代码,使其在计算出平均分后,能够动态更新圆形进度条的进度,从而提供更直观的用户体验。本文档包含详细的代码示例和注意事项,帮助开发者轻松实现这一功能。…

    2026年5月10日
    000
  • Go语言接口与切片:如何识别和操作[]interface{}

    本文将深入探讨Go语言中如何识别和操作`[]interface{}`类型的切片。我们将介绍类型断言(Type Assertion)的关键作用,并通过`switch`语句演示如何安全地检测`[]interface{}`类型,并进而遍历其内部元素。文章旨在提供清晰的示例代码和专业指导,帮助开发者有效地处…

    2026年5月10日
    000
  • JavaScript计算器开发:解决数值显示与初始化问题

    本教程深入探讨了使用JavaScript构建计算器时常见的数值显示异常问题,特别是由于类属性未初始化导致的`Cannot read properties of undefined`错误。我们将详细分析问题根源,并通过在构造函数中调用初始化方法来解决该问题,同时优化显示逻辑,确保计算器功能稳定且界面显…

    2026年5月10日
    000
  • Circle为何在凌晨向Solana新增铸造5亿枚USDC?USDC增发原因与对SOL生态影响深度解析

    近日,链上数据显示,Circle 在凌晨向 Solana 链新增铸造了 5亿枚USDC。此次大规模增发引起市场关注,投资者需要了解背后的原因以及对 Solana 生态的潜在影响。 USDC增发原因分析 增发 USDC 的主要原因可能包括: 满足市场需求:近期 Solana 上交易活动活跃,USDC …

    2026年5月10日
    000
  • 使用 Ajax 和 FormData 实现文件上传及文本数据提交的完整教程

    本文旨在解决在使用 Ajax 和 FormData 进行文件上传时,遇到的 $_POST 和 $_FILES 为空的问题。通过详细的代码示例和解释,我们将展示如何正确地构建 FormData 对象,并通过 Ajax 将文件和文本数据发送到服务器端,同时避免常见的错误配置,确保数据能够成功地被 PHP…

    2026年5月10日
    000
  • JavaScript 高效判断页面所有复选框状态的技巧与实践

    本文旨在提供一套高效且专业的javascript方法,用于判断网页中所有复选框的选中状态。我们将探讨如何利用`array.some()`快速确定是否有未选中的复选框(进而判断是否全部选中),以及如何使用`array.filter()`统计选中和未选中的复选框数量。通过优化dom元素选择和数组操作,提…

    2026年5月10日
    000
  • 解决Persistent UTM代码导致链接意外添加问号的问题

    本文旨在解决在使用JavaScript持久化UTM参数时,链接在没有UTM参数的情况下被意外添加问号的问题。通过分析问题代码,找出错误原因,并提供修正后的代码示例,确保只有当存在UTM参数时,链接才会被添加相应的参数。同时,强调了代码的健壮性和可维护性,避免不必要的修改和潜在的错误。 在使用Java…

    2026年5月10日
    200

发表回复

登录后才能评论
关注微信