Android 接口返回Json解析

基于最原始、最愚蠢的网络请求返回包括网络请求状态,引发的一些思考
( 只需要编写body部分,而且body对应的bean可以通过泛型传入,代替Class作为参数传入,将解析逻辑抽取并做到可配置,且可分类配置 )

举个栗子先

// 举个栗子,下面这个Json作为我们要解析的返回数据
{
    "header": {
         "code": "SUCCESS",
         "message": "响应成功"
    },
    "body": {
        "verificationNum": "XX数量",
        "info": [
            {
                "prodName": "产品名称",
                "count": "份数",
                "qrcode": "券号",
                "verificationTime": "XX时间"
            }
        ]
    }
}

原始的解析

最原始的解析Demo:

    public <T> void requestNetOld(final HashMap<String, String> requestMap,
                                     final String url, final Class<? extends BaseBean> clz, final NetCallBack<T> netCallBack) {
        new Thread() {
            @Override
            public void run() {
                super.run();
                // 网络请求
                String result = postDownloadJson(requestMap, url);
                if (netCallBack == null)
                    return;
                final T orderState = (T) JSON.parseObject(result, clz);
                mHandler.post(new Runnable() {

                    @Override
                    public void run() {
                        netCallBack.onBack(orderState);
                    }
                });
            }
        }.start();
    }

看看这个BaseBean:

public abstract class BaseBean {

    private Header header;
    // ......
    public static class Header{
        private String code;
        private String message;
        // ......
    }
}

下面是具体的实现类:

    public static class DemoBean extends BaseBean{

        private Body body;

        public Body getBody() {
            return body;
        }

        public void setBody(Body body) {
            this.body = body;
        }
    }

    public static class Body{
        // ......
    }

来模拟下请求:

        NetUtils.getInstance().requestNetOld(hashMap, url, DemoBean.class, new NetCallBack<DemoBean>() {
            public void onBack(DemoBean bean){

            }
        });

痛点

恶心的Demo终于结束了,这里有几个痛点:

  • DemoBean.class作为参数,每次都得传,麻烦。
  • 每次请求除了编写Body类还需要编写一个继承与BaseBean看起来毫无意义的包装类。

不需要传Class类型了

我们经常会使用到泛型,考虑泛型传递Class类型进行json解析,这个思路很多人觉得行不通,因为Java的泛型只存在于编译器,运行期Class上的泛型就被擦除了,属于假泛型。
是的,Class类上的泛型运行期确实没有任何泛型信息了,而且即便是给对象的泛型变量赋上具体值,再debug这个对象的这个字段的时候,会发现还是Object类型,所以Class上没有,对象的成员变量上也没有,还有一个思虑是能够动态构建出一个不存在的class,就是几个class拼接成一个新的class,这个想法也是无法实现的,Java反射之后只能拿到类型,修改Type里面的类型,其实并没有真正改变Class类型,因为Java是静态语言

[ 修正 start……

修正:Java是准动态语言,体现在几个方面:
1.反射机制
2.动态编译
3.动态执行javascript代码
4.动态字节码操作
5.动态转换类型

  1. 反射:只能动态修改对象属性,动态调用对象方法,动态生成对象,于Class无效。
  2. 动态编译:(此处无效)
    • 可以做一个浏览器端编写java代码,上传服务器编译和运行的在线评测系统,需要进行安全检查。
    • 服务器动态加载某些类文件进行编译
  3. 动态执行javascript代码:在这里无用。
  4. 动态字节码操作:( 很明显这种方式是可行的,应用也很帅气,只是苦于囊中羞涩——还不会~~ )
  5. 动态转换类型:从叫法上将貌似是我们需要的,Java自带这个功能,并且Java8经过了改进
    直接上代码了:
public class NetCallBack3<T> {

    public T body;
    // ......
}
        NetCallBack3 netCallBack3 = new NetCallBack3();
        netCallBack3.setBody(new TextView(this));
        TextView.class.cast(netCallBack3.getBody());
        netCallBack3.body.toString();
        // 仍要进行强转
        TextView cBody = (TextView) netCallBack3.body;
        try {
            Field body = netCallBack3.getClass().getDeclaredField("body");
            String name = body.getType().getName();
            System.out.print(name);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

这里给body进行的具体赋值,并且将body进行了强转,但后面获取body引用给TextView的时候仍然需要进行强转,看看debug数据,发现class中的body类型还是object,这里的运行时强转,仅仅是针对对象的。
这里写图片描述

……修正 end ]

我从JavaEE同学的博客上知晓了一种方法可以在运行期拿到泛型类型(Hibernate 上有应用),就是创建对象时,其类上的泛型,可以通过父类的class的某个参数得到:

((Class<T>) (((ParameterizedType) (getClass().getGenericSuperclass())).getActualTypeArguments()[0]));

具体代码:

public abstract class NetCallBack2<T> {
    private Class<T> tClass;

    @SuppressWarnings("unchecked")
    public Class<T> getTClz() {
        if (tClass == null) {
            tClass = ((Class<T>) (((ParameterizedType) (this.getClass()
                    .getGenericSuperclass())).getActualTypeArguments()[0]));
        }
        return tClass;
    }
}

调用:

        NetCallBack2<TextView> netCallBack = new NetCallBack2<TextView>() {};
        Log.e("tag" ,netCallBack.getTClz().getName());

Log输出:

09-22 20:10:37.732 14149-14149/com.turbo.test E/tag: android.widget.TextView

得到了吧~~~,注意这里必须是创建类对象代码(至少是抽象类),并且要从父类类对象参数中获取

这个泛型可以用到抽象的回调上,完美!!

只需要编写Body部分的JavaBean

这个方法大多数人都知道,仅仅是我还是没想到(蠢了蠢了~~),解决办法就是先手动解析出Header部分以及Body部分,然后将body、header部分分别进行json解析。

public static String getBodyStr(String dataFormServer){
            JSONObject jsonObject = null;
            try {
                jsonObject = new JSONObject(dataFormServer);
                return jsonObject.optJSONObject("body").toString();
            } catch (JSONException e) {
                e.printStackTrace();
            }
            return errorJson;
        }
String bodyStr = XXXX.getDataStr(response);
TTTTTT reportNewBean = new Gson().fromJson(bodyStr , TTTTTT.class);

Header部分同理,解析成对应Bean并且设置到逻辑流程中去,最好将Header的解析设置成可配置模块,解析返回一个HeaderConfig对象给请求框架即可。

附加(泛型):

看下泛型的跨类跨方法传递:

以下伪代码:

public class FirstDoor<T> {

    SecondDoor<T> secondDoor;
    public void firstMethod(CallBack<T> callBack){
        secondDoor = new SecondDoor<T>();
        secondDoor.setCallBack(callBack);
        secondDoor.callBack1((T) new Object());
    }

    public SecondDoor<T> getSecondDoor(){
        return secondDoor;
    }

}
class SecondDoor<T> {
    private CallBack<T> callBack;

    public void setCallBack(CallBack<T> callBack) {
        this.callBack = callBack;
    }

    public void setCallBack2(CallBack<T> callBack) {}

    public void callBack1(T t) {
        callBack.callback(t);
    }
}
interface CallBack<T> {
    public void callback(T t);
}
        FirstDoor<TextView> firstDoor = new FirstDoor<>();
        firstDoor.firstMethod(new CallBack<TextView>() {
            @Override
            public void callback(TextView textView) {

            }
        });
        SecondDoor<TextView> secondDoor = firstDoor.getSecondDoor();
        firstDoor.getSecondDoor().setCallBack2(new CallBack<TextView>() {
            @Override
            public void callback(TextView textView) {

            }
        });

最后这段调用还是自动生成了CallBack的类泛型,自动提示生成了callback方法的泛型参数。泛型跨类跨方法没有问题。

泛型小知识:

List<? extends Number> eList = null;
eList = new ArrayList<Integer>();
Number numObject = eList.get(0);  //语句1,正确

//Type mismatch: cannot convert from capture#3-of ? extends Number to Integer
Integer intObject = eList.get(0);  //语句2,错误

//The method add(capture#3-of ? extends Number) in the type List<capture#3-of ? extends Number> is not applicable for the arguments (Integer)
eList.add(new Integer(1));  //语句3,错误
List<? super Integer> sList = null;
sList = new ArrayList<Number>();

//Type mismatch: cannot convert from capture#5-of ? super Integer to Number
Number numObj = sList.get(0);  //语句1,错误

//Type mismatch: cannot convert from capture#6-of ? super Integer to Integer
Integer intObj = sList.get(0);  //语句2,错误

sList.add(new Integer(1));  //语句3,正确

? super T 表示一个特定的类,这个类可能是T,也可能是T的父类或者超类,甚至OTject

  • 你可以将T或者T的子类的实例赋值给这个”变量”,因为T或者T的子类 肯定是一个OTject(或者其他任何T的超类)
  • 但你无法将这个”变量”赋值给其他”变量”,因为无法确定这个”变量”的具体类型

? extends T 表示一个特定的类,这个类可能是T,也可能是T的任意子类

  • 你可以把这个”变量”赋值给任意用T或者T的超类声明的”变量”
  • 但你无法为这个”变量”赋值,因为根本无法知道这个”变量”的类到底是T还是T的哪个子类

有人可能疑惑为何extends add会失败,而super add成功,举个栗子就知道了:

        List<TextView> list = new ArrayList<>();
        list.add(new Button(this));
        list.add(new EditText(this));
        list.add(new AppCompatButton(this));

其实add的内容是<>内部总范围的子类即可,到此上面全部得到解释。

PS 其实Gson也是这么做的:

public class TypeToken<T> {
    final Class<? super T> rawType;
    final Type type;
    final int hashCode;

    protected TypeToken() {
        this.type = getSuperclassTypeParameter(this.getClass());
        this.rawType = Types.getRawType(this.type);
        this.hashCode = this.type.hashCode();
    }
    static Type getSuperclassTypeParameter(Class<?> subclass) {
        Type superclass = subclass.getGenericSuperclass();
        if(superclass instanceof Class) {
            throw new RuntimeException("Missing type parameter.");
        } else {
            ParameterizedType parameterized = (ParameterizedType)superclass;
            return Types.canonicalize(parameterized.getActualTypeArguments()[0]);
        }
    }
    public final Class<? super T> getRawType() {
        return this.rawType;
    }
}
public static Class<?> getRawType(Type type) {
        if(type instanceof Class) {
            return (Class)type;
        } else if(type instanceof ParameterizedType) {
            ParameterizedType className2 = (ParameterizedType)type;
            Type rawType = className2.getRawType();
            Preconditions.checkArgument(rawType instanceof Class);
            return (Class)rawType;
        } else if(type instanceof GenericArrayType) {
            Type className1 = ((GenericArrayType)type).getGenericComponentType();
            return Array.newInstance(getRawType(className1), 0).getClass();
        } else if(type instanceof TypeVariable) {
            return Object.class;
        } else if(type instanceof WildcardType) {
            return getRawType(((WildcardType)type).getUpperBounds()[0]);
        } else {
            String className = type == null?"null":type.getClass().getName();
            throw new IllegalArgumentException("Expected a Class, ParameterizedType, or GenericArrayType, but <" + type + "> is of type " + className);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值