美文网首页
Android使用Gson的总结

Android使用Gson的总结

作者: 奋斗小青年Jerome | 来源:发表于2018-01-04 11:51 被阅读2102次

    先说一下gson的使用方法,可以参考这篇帖子,写的很详细,感谢作者,以下内容是根据原作者帖子进行总结的自己的笔记

    简要总结

    1. Gson提供了fromJson() 和toJson() 两个直接用于解析和生成的方法,前者实现反序列化,后者实现了序列化。同时每个方法都提供了重载方法,我常用的总共有5个。
    Gson gson = new Gson();
    int i = gson.fromJson("100", int.class);              //100
    double d = gson.fromJson("\"99.99\"", double.class);  //99.99
    boolean b = gson.fromJson("true", boolean.class);     // true
    String str = gson.fromJson("String", String.class);   // String
    
    1. 使用@SerializedName 注解重新命名字段名称
      这对于使用PHP作为后台开发语言时很常见的情况,php和js在命名时一般采用下划线风格,而Java中一般采用的驼峰法,让后台的哥们改吧 前端和后台都不爽,但要自己使用下划线风格时我会感到不适应,怎么办?难到没有两全齐美的方法么?
      我们知道Gson在序列化和反序列化时需要使用反射,说到反射就不得不想到注解,一般各类库都将注解放到annotations包下,打开源码在com.google.gson包下果然有一个annotations,里面有一个SerializedName的注解类,这应该就是我们要找的。
      那么对于json中email_address这个属性对应POJO的属性则变成
    @SerializedName("email_address")
    public String emailAddress;
    

    对于有多个字段都需要重新命名时,SerializedName注解提供了两个属性,上面用到了其中一个,别外还有一个属性alternate,接收一个String数组。
    注:alternate需要2.4版本

    @SerializedName(value = "emailAddress", alternate = {"email", "email_address"})
    public String emailAddress;
    

    当上面的三个属性(email_address、email、emailAddress)中出现任意一个时均可以得到正确的结果

    1. 泛型擦除
      Gson解析数组的时候,比较方便,例如
    ["Android","Java","PHP"]
    

    直接解析

    Gson gson = new Gson();
    String jsonArray = "[\"Android\",\"Java\",\"PHP\"]";
    String[] strings = gson.fromJson(jsonArray, String[].class);
    

    但对于List将上面的代码中的 String[].class 直接改为 List<String>.class 是行不通的。对于Java来说List<String> 和List<User> 这俩个的字节码文件只一个那就是List.class,这是Java泛型使用时要注意的问题 泛型擦除
    为了解决的上面的问题,Gson为我们提供了TypeToken来实现对泛型的支持,所以当我们希望使用将以上的数据解析为List<String>时需要这样写。

    Gson gson = new Gson();
    String jsonArray = "[\"Android\",\"Java\",\"PHP\"]";
    String[] strings = gson.fromJson(jsonArray, String[].class);//解析成数组
    List<String> list = gson.fromJson(jsonArray, new TypeToken<List<String>>() {}.getType());//解析成集合
    
    1. 针对服务器返回数据的封装
      有下面两种数据,我们需要的是data,code只用一次,message基本不用,那么针对data,第一个是bean,第二个则是数组,为了形象,暂且理解为第一个是User,第二个是List<User>
    "code":"0","message":"success","data":{}}
    
    {"code":"0","message":"success","data":[]}
    

    那么不适用泛型封装的时候,我们的字典类一定是这么写

    public class UserResponse {
        public int code;
        public String message;
        public User data;
    }
    
    public class UserResponseList {
        public int code;
        public String message;
        public List<User> list;
    }
    

    蓝后,我们的解析是这么写

    Gson gson = new Gson();
    //解析bean
    UserResult userResult = gson.fromJson(json,UserResponse .class);
    User user = userResult.data;
    //解析集合
    UserResponseList userList= gson.fromJson(json,UserResponseList .class);
    List<User> users = userList.data;
    

    所以如果一两个还行,等真正上项目的时候,那就无语了;那么使用泛型针对返回数据进行封装了?
    可以看到,公共的字段是code和message,也就是说每个后台返回来的json数据格式都是这种格式,我们就可以把公共的部分抽取出来,至于data我们不知道到底返回的是单独的字典,还是集合,还是数组,所以采用泛型

    public class Result<T> {
        public int code;
        public String message;
        public T data;
    }
    

    这时候对于data字段是User时则可以写为 Result<User> ,当是个列表的时候为 Result<List<User>>,其它同理。
    解析的时候

    Type userType = new TypeToken<Result<User>>(){}.getType();
    Result<User> userResult = gson.fromJson(json,userType);
    User user = userResult.data;
    
    Type userListType = new TypeToken<Result<List<User>>>(){}.getType();
    Result<List<User>> userListResult = gson.fromJson(json,userListType);
    List<User> users = userListResult.data;
    

    这样就解决了服务器返回数据的封装

    1. Gson的一些配置
    Gson gson = new GsonBuilder()
            //序列化null
            .serializeNulls()
            // 设置日期时间格式,另有2个重载方法
            // 在序列化和反序化时均生效
            .setDateFormat("yyyy-MM-dd")
            // 禁此序列化内部类
            .disableInnerClassSerialization()
            //生成不可执行的Json(多了 )]}' 这4个字符)
            .generateNonExecutableJson()
            //禁止转义html标签
            .disableHtmlEscaping()
            //格式化输出
            .setPrettyPrinting()
            .create();
    
    1. 基于Gson系列化json的踩坑
      在三星note2(Android4.3)手机上面,使用webView.loadUrl(url)这个方法给js回传一个String类型的base64图片,发现js根本就没有拿到这个base64,经过debug,发现使用Gson.toJson方法,生成的字符串自动给价格换行符\n,各种找了一圈都没有发现问题,最后在Gson初始化这里
     public static Gson getGson() {
            if (null == gson) {
                synchronized (GsonUtils.class) {
                    if (null == gson) {
                        GsonBuilder builder = new GsonBuilder().disableHtmlEscaping().setPrettyPrinting();
                        gson = builder.setDateFormat("yyyy-MM-dd HH:mm:ss").create();//.setFieldNamingStrategy(new AnnotateNaming()).create();
                    }
                }
            }
            return gson;
        }
    

    发现后面多了个.setPrettyPrinting();这个方法,意思是格式化输出,去掉这个,\n没有了,再次传的时候发现问题解决
    这个问题在4.4及以上没有发现,应该是系统问题,4.3及以前的webView用的是WebKit内核,从4.4开始使用的是Chrome内核,4.3的loadUrl方法传进去的字符串不能含有转义?等翻了源码再确定一下这个问题

    总结

    通过踩坑,总结几点:

    1. 在做适配的时候,首先要排除系统问题,认真记录一下操作系统的版本
    2. 做最小化测试,首先从最简单的开始排除;
      例如此坑中,传递的base64字符串特别大,算了一下大概有8万个字符,刚开始怀疑是字符太多,就做实验,写了一个循环,使用StringBuilder每次添加1万个字符进去,发现到10万都没问题,这样就排除了大小限制;然后再就是排除特殊符号,比如'' /r/n \r\n 等等;一个个去排除;最后发现就是特殊字符引起的.
      3.认真做好每次踩坑的笔记

    相关文章

      网友评论

          本文标题:Android使用Gson的总结

          本文链接:https://www.haomeiwen.com/subject/kfzlnxtx.html