当前位置:首页 > 技术 > 正文内容

Retrofit 动态支持多数据格式:自定义 ConverterFactory 同时解析 JSON 与 XML

访客 技术 2026年7月7日 1

在 Retrofit 中,addConverterFactory() 方法用于配置数据转换器,从而将 HTTP 响应体自动反序列化为 Java 对象。针对常见的 JSON 和 XML 数据格式,官方分别提供了 GsonConverterFactorySimpleXmlConverterFactory

依赖配置与已知问题

引入对应的转换器依赖:

implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.retrofit2:converter-simplexml:2.9.0'

注意:SimpleXmlConverterFactory 官方已标记为废弃,建议使用 converter-jaxb。但在实际 Android 项目中,JAXB 转换器经常会出现兼容性问题,因此许多项目仍在使用 SimpleXML 或迁移至其他 XML 解析库。本文以 SimpleXML 为例进行演示。

多转换器冲突现象

Retrofit 的转换器链采用"责任链"模式,但在处理 JSON 和 XML 时,由于底层解析器的严格校验机制,通常无法简单地叠加使用。例如:

Retrofit client = new Retrofit.Builder()
    .baseUrl(BASE_URL)
    .addConverterFactory(SimpleXmlConverterFactory.create())
    .addConverterFactory(GsonConverterFactory.create())
    .build();

在上述配置中,XML 转换器被优先添加。当请求返回 JSON 数据时,XML 解析器会尝试解析并抛出异常,导致请求失败,而不会将解析权移交后续的 Gson 转换器。反之,若将 Gson 放在前面,解析 XML 时同样会崩溃。简而言之,只有排列在首位的转换器能够生效。

解决方案:基于注解的动态路由

为了让同一个 Retrofit 实例能够根据具体接口智能选择解析器,我们可以通过自定义注解来标记接口的预期返回格式,并编写一个聚合型的 Converter.Factory 进行路由分发。

1. 定义数据格式注解

创建一个用于标记方法响应类型的注解:

import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 标记接口响应的数据格式
 */
@Target(METHOD)
@Retention(RUNTIME)
public @interface ResponseDataType {
    String FORMAT_JSON = "json";
    String FORMAT_XML = "xml";

    String value() default FORMAT_JSON;
}

2. 实现自定义 ConverterFactory

构建一个内部持有 JSON 和 XML 转换器实例的工厂类,在 responseBodyConverter 方法中读取注解并分发请求:

import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.simplexml.SimpleXmlConverterFactory;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

public class DynamicFormatConverterFactory extends Converter.Factory {

    private final Converter.Factory jsonDelegate = GsonConverterFactory.create();
    private final Converter.Factory xmlDelegate = SimpleXmlConverterFactory.create();

    public static DynamicFormatConverterFactory create() {
        return new DynamicFormatConverterFactory();
    }

    @Override
    public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        for (Annotation annotation : annotations) {
            if (annotation instanceof ResponseDataType) {
                String format = ((ResponseDataType) annotation).value();
                if (ResponseDataType.FORMAT_XML.equals(format)) {
                    return xmlDelegate.responseBodyConverter(type, annotations, retrofit);
                } else {
                    return jsonDelegate.responseBodyConverter(type, annotations, retrofit);
                }
            }
        }
        // 默认回退到 JSON 解析
        return jsonDelegate.responseBodyConverter(type, annotations, retrofit);
    }
}

3. 接口定义与客户端构建

在 API 接口中使用自定义注解,并配置 Retrofit 实例:

import io.reactivex.Observable;
import retrofit2.http.GET;

public interface MultiFormatService {

    @GET("api/v1/user/profile")
    @ResponseDataType(ResponseDataType.FORMAT_JSON)
    Observable<UserProfile> fetchJsonProfile();

    @GET("api/v1/config/settings")
    @ResponseDataType(ResponseDataType.FORMAT_XML)
    Observable<AppSettings> fetchXmlSettings();
}
// 构建 Retrofit 实例
Retrofit retrofitClient = new Retrofit.Builder()
        .baseUrl("https://api.example.com/")
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .addConverterFactory(DynamicFormatConverterFactory.create())
        .build();

// 发起请求
MultiFormatService service = retrofitClient.create(MultiFormatService.class);

service.fetchJsonProfile()
    .subscribe(profile -> {
        // 处理 JSON 解析后的对象
    });

service.fetchXmlSettings()
    .subscribe(settings -> {
        // 处理 XML 解析后的对象
    });

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。