使用gson配合Retrofit2解析服务器返回

项目使用了Retrofit2+rxjava2+gson 处理网络请求的数据处理,这里主要介绍gson部分。

一、配置gson

retrofit2有专门的converter只要依赖以后就可以使用gson啦。

compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'

然后需要在新建retrofit类时addConverterFactory,如果不需要使用特定的Gson,比如添加自己的TypeAdpter特殊处理的话,可以不传gson对象,factory会自己新建一个。

@Singleton
@Provides
Retrofit provideRetrofit(OkHttpClient client, Gson gson){
    return new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create(gson))
            .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
            .client(client)
            .build();
}

这样gson就初步配置好啦。

二、使用gson。

1.分析服务器数据编写对应的bean文件。

public class Bean<T> {

    private String msg;
    @SerializedName(value = "status", alternate = {"Status"})
    private int resultCode;
    @SerializedName("data")
    private T result;
}
这个bean对应的是json结构是{"data":[],"msg":"","status":0}
如果返回的数据名和bean文件的名称不同,可以设置@SerializedName的value值来接受
如果同样的bean,中的同一个数据有两种返回方式,比如:status和Status,那就可以通过设置alternate来特殊处理。
这里data中的数据使用了泛型,就可以根据不同场合的泛型嵌套处理json数据,比如Bean>,可以嵌套很多层来满足不同的需要,达到bean复用的目的。

2.解析
然后在配置Retrofit2的设置中,写明接口的返回:
@FormUrlEncoded
@POST(value = "login.html")
Observable>> login(@Field("token") String token, @Field("username") String user, @Field("password") String passWord);

Result类是 com.jakewharton.retrofit2.adapter. rxjava2包提供的统一的数据返回类。

在访问成功后,我们就可以得到
Result形式的对象了,可以方便的从中获得数据。
通过
Bean<T> b =  result.response().body();
得到Bean对象。

三、复杂情况处理

在具体业务处理的过程中,会遇到同样的字段,在不同情况下服务器返回不同类型的情况。

比如:上文中的bean中的data数据,在不同的情况下可能是jsonobject也可能是String也可能是jsonArray。
解决方式:
1、可以定义data为Object形式。
2、上文中的bean是作为一个统一的外壳,来存放一些服务器返回的状态,而data中才是具体的业务数据,所以data需要设置为泛型来表示不同情况下的
数据对象。
这种情况下我们可以使用Gson提供的 TypeAdapter或者JsonDeserializer,JsonSerializer来做特殊处理
TypeAdater使用相对复杂,能处理序列化和反序列化两种情况。

JsonDeserializer,JsonSerializer分别用来处理反序列化和序列化的情况,使用起来容易一些,但会牺牲一些性能。

我们这里主要处理服务器端的返回数据,所以选择使用JsonDeserializer来做特殊处理。

public class BeanTypeDeserializer implements JsonDeserializer {
    @Override
    public Bean deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
 
  
	JsonObject obj = json.getAsJsonObject();
return new Bean<>(obj.get( "msg" ).getAsString() , obj.get( "status" ).getAsInt() ,new Object()) ; }} 虽然,bean带有泛型,但是在编译期间泛型便会被擦除,这里没必要写成>的形式,除非Bean中的泛型是确定的,比如>这种形式。

JsonElement是json数据。

Type就是处理数据得类型,这里应该是Bean的格式,XXX就是data具体的类。

JsonDeserializationContext  是反序列化的上下文。


上面做的是一个简单处理,只是忽略了返回中的data数据,传了一个Object。这显然是无法满足需要的。


因为,data中的数据是多种多样的类,我们不可能自己一一处理。

这里就需要用到JsonDeserializationContext ,将data中的json重新交给Gson去做进一步处理,这是一个接口对象只有一个方法:

public <T> T deserialize(JsonElement json, Type typeOfT) throws JsonParseException;
我们需要传入jsonElement数据,Type(我们希望转换得到的类,也就是这个方法的返回T的类型)

jsonElement我们可以通过obj.get()得到。

而Type,在这个情况下其实是不确定的,一开始我考虑传入T,通过Gson提供的TypeToken获取。

public class BeanTypeDeserializer<T> implements JsonDeserializerT>> {
    @Override
    public Bean deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        JsonObject obj = json.getAsJsonObject();
        context.deserialize(obj.get("data"), new TypeToken<T>() {}.getType());
}  
可惜这并不能正常工作,因为T在编译时已经被擦除了,而我们在new  BeanTypeDeserializer时是不可能传入T的具体类的,在运行中这个T会是各种类型。

这里我们就要使用传入的typeOfT,正确的解决方式是:

context.deserialize(obj.get("data"), ((ParameterizedType) typeOfT).getActualTypeArguments()[0])

最终的解决方式:

public class BeanTypeDeserializer<T> implements JsonDeserializerT>> {
    @Override
    public Bean deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        JsonObject obj = json.getAsJsonObject();
        int status = obj.get("status").getAsInt();
        if(status==0){
            return new Bean<>(obj.get("msg").getAsString(),status, context.deserialize(obj.get("data"), ((ParameterizedType) typeOfT).getActualTypeArguments()[0]));
        }else {
            return new Bean<>(obj.get("msg").getAsString(),status,new Object());
        }
    }
}
我这里,在status不为0时,代表了服务器不是正常返回,所以我不处理data的数据,这样就可以避免出错。

配置BeanTypeDeserializer

@Provides
Gson provideGson(){
    return new GsonBuilder().registerTypeAdapter(Bean.class, new BeanTypeDeserializer()).create();
}
调用registerTypeAdapter方法
public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter)
Type就是注册对那个类做处理,后面就是我们的处理类。
因为这里可以使用 TypeAdapter或者JsonDeserializer,JsonSerializer,Gson使用Object进行的接收。


以上就是我在使用Gson配置Retrofit2处理网络返回的所有使用方式,暂时能够解决我遇到的所有情况,如果还有其他特殊情况,欢迎大家留言讨论。


你可能感兴趣的:(android)