先说一下gson的使用方法,可以参考这篇帖子,写的很详细,感谢作者,以下内容是根据原作者帖子进行总结的自己的笔记
简要总结
- 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
- 使用@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)中出现任意一个时均可以得到正确的结果
- 泛型擦除
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());//解析成集合
- 针对服务器返回数据的封装
有下面两种数据,我们需要的是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;
这样就解决了服务器返回数据的封装
- Gson的一些配置
Gson gson = new GsonBuilder()
//序列化null
.serializeNulls()
// 设置日期时间格式,另有2个重载方法
// 在序列化和反序化时均生效
.setDateFormat("yyyy-MM-dd")
// 禁此序列化内部类
.disableInnerClassSerialization()
//生成不可执行的Json(多了 )]}' 这4个字符)
.generateNonExecutableJson()
//禁止转义html标签
.disableHtmlEscaping()
//格式化输出
.setPrettyPrinting()
.create();
- 基于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方法传进去的字符串不能含有转义?等翻了源码再确定一下这个问题
总结
通过踩坑,总结几点:
- 在做适配的时候,首先要排除系统问题,认真记录一下操作系统的版本
- 做最小化测试,首先从最简单的开始排除;
例如此坑中,传递的base64字符串特别大,算了一下大概有8万个字符,刚开始怀疑是字符太多,就做实验,写了一个循环,使用StringBuilder每次添加1万个字符进去,发现到10万都没问题,这样就排除了大小限制;然后再就是排除特殊符号,比如'' /r/n \r\n 等等;一个个去排除;最后发现就是特殊字符引起的.
3.认真做好每次踩坑的笔记