Android 解决Gson解析数据类型不匹配的崩溃问题 JSONException expect beauty but ....

假设,我们请求服务器后,正常的返回的数据结构是这样的:

{
    "result": [
        {
            "user": "test",
            "age": 30
        }
    ],
    "code": "0000",
    "message": "操作成功"
}

但是如果,某些现实情况,返回的数据结构变成以下这样:

{
    "result": [
        {
            "user": "test",
            "age": "30"
        }
    ],
    "code": "0000",
    "message": "操作成功"
}

在上面,result 数组中的对象里,age字段的数据类型由开始的整型30,变成了字符串类型 “30”,如无意外的话,这个对象会解析失败,然后就会导致界面显示的数据不完整等其他情况,因为解析过程被迫中断了。

那么,我们应该怎么做呢?

首先,在构建 Retrofit 实例的时候,一般是类似以下的代码:

mRetrofit = new Retrofit.Builder()
                    .client(mOkHttpClient)//设置自定义okHttp
                    .baseUrl(Constants.BASE_URL) //基础地址
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())  //配置RxJava适配
                    .addConverterFactory(GsonConverterFactory.create())//添加原装GSON解析
                    .build();

那么,这部分代码中,里面的 addConverterFactory  这个方法是用来添加 json 解释器的,我们要改动的地方就在这里。将上面的代码改成以下代码:

mRetrofit = new Retrofit.Builder()
                    .client(mOkHttpClient)//设置自定义okHttp
                    .baseUrl(Constants.BASE_URL)
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())  //配置RxJava适配
                    .addConverterFactory(ResponseConverterFactory.create())//添加自定义GSON解析
                    .build();

你会发现,json 解释器我将  retrofit 提供的原生的  GsonConverterFactory  改成了 自定义的ResponseConverterFactory 。那么, 这个 ResponseConverterFactory  是哪里来的呢?来自如下的代码:

public class ResponseConverterFactory extends Converter.Factory {
    private final Gson gson;

    private ResponseConverterFactory(Gson gson) {
        if (gson == null) throw new NullPointerException("gson == null");
        this.gson = gson;
    }


    public static ResponseConverterFactory create() {
        return create(new GsonBuilder().registerTypeAdapterFactory(new MyTypeAdapterFactory()).create());
    }

    public static ResponseConverterFactory create(Gson gson) {
        return new ResponseConverterFactory(gson);
    }


    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        return new GsonResponseBodyConverter<>(gson,type);
    }
}

解释一下,上述代码中的自定义类分别有:

MyTypeAdapterFactory:数据类型相关的,例如 int ,float,string等等,你要处理什么数据类型,就将对应的丢进去

GsonResponseBodyConverter:定义了处理json数据的对象类(例如Gson)以及实例化目标的数据类(例如student对象类),

我们先看下 MyTypeAdapterFactory的代码:

public class MyTypeAdapterFactory<T> implements TypeAdapterFactory {
    @SuppressWarnings("unchecked")
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        Class<T> rawType = (Class<T>) type.getRawType();
        if (rawType == Float.class || rawType == float.class) {
            return (TypeAdapter<T>) new FloatNullAdapter();
        }else if (rawType == Integer.class || rawType == int.class){
            return (TypeAdapter<T>) new IntNullAdapter();
        }
        return null;
    }
}

代码意思大概就是,当数据匹配到的类型的是 float (或者Float)类型的话,就返回一个 FloatNullAdapter 浮点型适配器这么一个东西,所以这个 FloatNullAdapter 是个啥东西

直接看代码:

public class FloatNullAdapter extends TypeAdapter<Float> {
    @Override
    public Float read(JsonReader reader) throws IOException {
        // TODO Auto-generated method stub
        if (reader.peek() == JsonToken.STRING) {
            reader.skipValue(); //跳过当前
            return -1f;
        }
        BigDecimal bigDecimal = new BigDecimal(reader.nextString());
        return bigDecimal.floatValue();
    }

    @Override
    public void write(JsonWriter writer, Float value) throws IOException {
        // TODO Auto-generated method stub
        writer.value(value);
    }
}

作用大概就是,当发现当前数据预先设置的类型是float类型,而接收到的数据是字符串类型时候,跳过当前的数据,并且返回一个固定值 -1 ,当然这是我的写法,具体场景请各位道友自行根据需求编写。

然后,IntNullAdapter :

public class IntNullAdapter extends TypeAdapter<Number> {
    @Override
    public Number read(JsonReader reader) throws IOException {
        if (reader.peek() == JsonToken.NULL) {
            reader.nextNull();
            return null;
        }
        try {
            return reader.nextInt();
        } catch (NumberFormatException e) {
            GyLog.e("NumberFormatException ->"+e.getMessage());
            //这里解析int出错,那么捕获异常并且返回默认值,因为nextInt出错中断了方法,没有完成位移,所以调用nextString()方法完成位移。
            reader.nextString();
            return 0;
        }
    }

    @Override
    public void write(JsonWriter out, Number value) throws IOException {
        out.value(value);
    }
}

其实大概思路也差不多

所以,我们直接看 GsonResponseBodyConverter:

public class GsonResponseBodyConverter<T> implements Converter<ResponseBody,T> {
    private final Gson gson;
    private final Type type;

    GsonResponseBodyConverter(Gson gson, Type type) {
        this.gson = gson;
        this.type = type;
    }

    /**
     * 针对数据返回成功、错误不同类型字段处理
     */
    @Override
    public T convert(ResponseBody value) throws IOException {
        String response = value.string();
        return gson.fromJson(response, type);
    }
}

关键点在于,gson 这货是来自构造函数参数里的,所以,可以返回到  ResponseConverterFactory  这个类里,因为 GsonResponseBodyConverter 是在ResponseConverterFactory 里被new了。然后,再跟下去,你会发现 gson 的实例是由 

GsonBuilder().registerTypeAdapterFactory(new MyTypeAdapterFactory()).create()

弄出来的,也就是我们定义了gson在处理 float 以及 int 类型数据的时候,要走 MyTypeAdapterFactory 里面的 FloatNullAdapter 以及 IntNullAdapter 两个类事先定义的逻辑,而不是之前默认的new Gson 出来的实例。

如此一来,也就达到了当初的需求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值