Retrofit请求XML接口 Android使用SimpleXmlConverter

2次阅读

simplexmlconverter在android 8.0+需配置network_security_config允许http明文流量;不支持list直接解析,须用@elementlist(inline=true)的根容器类;需排除xpp3冲突、确保无参构造函数、注意kotlin字段可见性及xml命名空间严格匹配。

Retrofit请求XML接口 Android使用SimpleXmlConverter

SimpleXmlConverter不支持Android 8.0+默认禁用HTTP明文流量

直接跑不起来,多半是这个原因。Android 8.0(API 26)起,默认禁止应用使用明文 HTTP 请求,而很多老 XML 接口还在用 http:// 地址。retrofit 本身不拦,但底层 okhttp 会直接抛 java.net.UnknownServiceException: CLEARTEXT communication to xxx not permitted by network security policy

实操建议:

  • 检查接口地址是否为 http:// —— 如果是,要么切到 https://,要么在 res/xml/network_security_config.xml 中显式允许该域名的明文通信
  • 别只改 AndroidManifest.xmlandroid:usesCleartextTraffic="true",它在 targetSdkVersion ≥ 28 时对部分域名(如 localhost)也不生效,必须配 network_security_config
  • SimpleXmlConverter 本身不处理网络层,它只管把响应体字节流反序列化成 Java 对象,所以网络不通、404、500 都不是它的问题

SimpleXmlConverterFactory.create() 默认不支持 List 直接解析

写个接口返回一串 <items><item>...</item><item>...</item></items>,然后定义 Call<list>></list>,大概率会崩:Gson 可以这么干,SimpleXML 不行——它需要一个**根容器类**,不能直接映射到裸 List

常见错误现象:org.simpleframework.xml.core.PersistenceException: constructor not matched for class java.util.ArrayList

实操建议:

  • 定义一个包装类,比如 ItemsResponse,里面用 @ElementList(inline = true) 声明 List<item> items</item>
  • inline = true 很关键:它告诉 SimpleXML 跳过外层 <items></items> 标签,直接从子节点开始解析;不加的话,XML 必须是 <itemsresponse><items><item>...</item></items></itemsresponse>
  • 别在 Item 类里漏掉无参构造函数——SimpleXML 反射实例化时强依赖它

SimpleXML 在 Android 上需手动排除 XPP3 冲突

项目里如果用了其他库(比如旧版 OkHttp、某些支付 SDK),很可能自带了 xpp3xstream,而 SimpleXML 依赖的 simple-xml 默认打包了它自己的 xpp3 实现。运行时可能报 NoClassDefFoundErrorVerifyError,尤其在 dalvik(低版本 Android)上更明显。

实操建议:

  • app/build.gradledependencies 里,对 simple-xmlexclude
    implementation('org.simpleframework:simple-xml:2.7.1') {     exclude group: 'xpp3', module: 'xpp3' }
  • 同时显式引入轻量兼容版:implementation 'xpp3:xpp3:1.1.4c'(别用 1.1.3.x,有已知 Android 字符集 bug
  • 如果用了 Kotlin,注意 @Root@Element 这些注解要加在 val 字段上,且字段不能是 private——SimpleXML 不读 Kotlin 编译后的 getter/setter

SimpleXML 解析失败时很难定位具体哪一行出错

不像 json 有明确的 offset 和 Token 错误,SimpleXML 报错经常是 Unexpected tokenExpected start tag,但没告诉你 XML 第几行、哪个标签。尤其当服务端返回 HTML 错误页(比如 404 时返回了 nginx 默认页)却当成 XML 解析,就更难排查。

实操建议:

  • 先用 OkHttp 的 LoggingInterceptor 打印原始响应体,确认返回的是真 XML,且编码是 UTF-8(SimpleXML 默认按 UTF-8 解码,服务端若用 GBK 会乱码)
  • 在 Converter 里临时加一层包装:继承 SimpleXmlResponseBodyConverter,在 convert()catch 异常后打印 response.body().String() 前 512 字符(注意只能读一次)
  • XML 命名空间(xmlns)必须严格匹配:服务端写了 xmlns="http://xxx",你的 @Root(Namespace = "http://xxx") 就不能少,也不能多空格

真正麻烦的从来不是怎么写注解,而是服务端 XML 格式松散、文档缺失、偶尔夹带 bom 或非法字符——这些没法靠加依赖解决,得靠日志和耐心对齐。

text=ZqhQzanResources