Retrofit2.0笔记——addConverterFactory同时支持xml和json格式响应数据

Retrofit2的一个特色功能,就是通过addConverterFactory()方法设置一个数据转换器,可以将http请求的响应数据转换成JavaBean。如后台常见的两种返回数据格式:json和xml,分别可以用GsonConverterFactorySimpleXmlConverterFactory进行转换。对应的gradle依赖分别是:

implementation 'com.squareup.retrofit2:converter-gson:2.x.x'
implementation 'com.squareup.retrofit2:converter-simplexml:2.x.x'

注:

SimpleXmlConverterFactory目前已被声明为Deprecated,推荐的替代方案为JAXB converter(JaxbConverterFactory),对应的gradle依赖是:

implementation 'com.squareup.retrofit2:converter-jaxb:2.x.x'

但经过实测,该转换器设置在Retrofit中后不能正常工作。


默认情况下,由于Gson(对于JSON)和SimpleXML(对于XML)转换器的行为,一个Retrofit实例不能同时支持这两个转换器。如果这样写:

Retrofit mRetrofit = new Retrofit.Builder()
                .baseUrl(URL)
                .addConverterFactory(SimpleXmlConverterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();

那么,转换xml格式的返回数据正常,转换json格式的返回数据则会崩溃。
如果这样写:

Retrofit mRetrofit = new Retrofit.Builder()
                .baseUrl(URL)
                .addConverterFactory(GsonConverterFactory.create())
                .addConverterFactory(SimpleXmlConverterFactory.create()
                .build();

那么,转换json格式的返回数据正常,转换xml格式的返回数据则会崩溃。
总之就是,只有先添加的那个可以正常使用。


那有没有可以同时正常转换json和xml格式的方案呢?肯定是有的!

解决思路

给Retrofit要调用的接口方法添加一个注解,说明该请求的响应数据格式。然后自定义一个ConverterFactory,根据注解的属性值决定是使用json转换器还是xml转换器。

实现代码

自定义注解,比如叫ResponseFormat:

/**
 * 接口返回的数据格式,当前限定取值:{@link #JSON}或{@link #XML}
 */
@Target(METHOD)
@Retention(RUNTIME)
public @interface ResponseFormat {

    String JSON = "json";

    String XML = "xml";

    String value() default "";
}

自定义ConverterFactory,比如叫JsonOrXmlConverterFactory:

public class JsonOrXmlConverterFactory extends Converter.Factory {

    private final Converter.Factory xmlFactory = SimpleXmlConverterFactory.create();
    private final Converter.Factory jsonFactory = GsonConverterFactory.create();

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

    @Nullable
    @Override
    public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        for (Annotation annotation : annotations) {
            if (!(annotation instanceof ResponseFormat)) {
                continue;
            }
            String value = ((ResponseFormat) annotation).value();
            if (ResponseFormat.JSON.equals(value)) {
                return jsonFactory.responseBodyConverter(type, annotations, retrofit);
            } else if (ResponseFormat.XML.equals(value)) {
                return xmlFactory.responseBodyConverter(type, annotations, retrofit);
            }
        }

        return null;
    }
}

测试代码

interface TestApi {
        @GET("testapi/getjson.php/")
        @ResponseFormat("json")
        Observable getJson();

        @GET("testapi/getxml.php/")
        @ResponseFormat("xml")
        Observable getXml();
    }

//……
Retrofit mRetrofit = new Retrofit.Builder()
               .baseUrl(URL)
               .addConverterFactory(JsonOrXmlConverterFactory.create()
               .build();
//……

public void getCode(Consumer consumer) {
    TestApi testApi = mRetrofit.create(TestApi.class);
    testApi.getJson()
            .subscribe(consumer);
}

public void getXml(Consumer consumer) {
    TestApi testApi = mRetrofit.create(TestApi.class);
    testApi.getXml()
            .subscribe(consumer);
}

总结

这个问题虽然有解决方案,但需求场景应该较少。一般后台服务,基于同一个baseUrl的api接口,一般都是统一响应数据格式。只有app中需要从不同的后台服务url请求数据,这样才可能出现多种格式的返回数据,Retrofit本身是支持多baseUrl的。

你可能感兴趣的:(Android开发)