Gson 快速开始!
Gson是谷歌搞出来的用来解析json的工具,运用了java的反射机制,他的功能主要有:
- 将java对象解析成json对象
- 将json对象解析成java对象
使用时需要在maven工程中导入相应依赖
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.1</version>
</dependency>
or
compile 'com.google.code.gson:gson:2.3.1'
解析
解析情况分为三种:
- 标量(Scalar),也就是单纯的字符串或则数字形式
- 序列(Sequence),也就是若干数据按照一定顺序并列在一起又称“数组”
- 映射(Mapping),也就是key/value键值对
JsonObject对象
将一个json串解析成一个JsonObject对象,对于获取其中一些简单的属性比较方便,免去了写映射类了
// json:{"tasks":[{"id":"123","name","pp"},{"id":"456","name","mm"}]}
JsonObject workInfoObj = gson.fromJson(workInfoObjStr,JsonObject.class);
String workTaskId = workInfoObj.get("tasks").getAsJsonArray().get(0).getAsJsonObject().get("id").getAsString();
JsonObject方法详情查看:https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/JsonObject.html
普通对象
采用映射方式,将JavaBean对象的属性值与json对象进行映射,注意:不匹配的属性不会被映射,字段值必须一样!
String json_str = "{"name":"kalen", "age":22}";
Gson gson = new Gson();
User user = gson.fromGson(json_str, User.class);
数组对象
//数组对应gson中的类型
Type listType = new TypeToken<List<String>>() {}.getType();
//gson需要的转换对象或则数据来源
List<String> target = new LinkedList<String>();
target.add("blah");
Gson gson = new Gson();
// 将list对象转为json对象
String json = gson.toJson(target, listType);
// 将json对象转为list对象
List<String> target2 = gson.fromJson(json, listType);
此处使用了泛型!
注意:当解析非基本数据类型和数组时,需要加上泛型!
Gson gson = new Gson();
String jsonArray = "[\"Android\",\"Java\",\"PHP\"]";
String[] strings = gson.fromJson(jsonArray, String[].class);
但对于List将上面的代码中的 String[].class
直接改为 List.class
是行不通的。对于Java来说List
和List
这俩个的字节码文件只一个那就是List.class
,这是Java泛型使用时要注意的问题 泛型擦除。
为了解决的上面的问题,Gson为我们提供了TypeToken
来实现对泛型的支持,所以当我们希望使用将以上的数据解析为List
时需要这样写。
Gson gson = new Gson();
String jsonArray = "[\"Android\",\"Java\",\"PHP\"]";
String[] strings = gson.fromJson(jsonArray, String[].class);
List<String> stringList = gson.fromJson(jsonArray, new TypeToken<List<String>>() {}.getType());
注:TypeToken
的构造方法是protected
修饰的,所以上面才会写成new TypeToken>() {}.getType()
而不是 new TypeToken>().getType()
泛型解析对接口POJO的设计影响
泛型的引入可以减少无关的代码,如我现在所在公司接口返回的数据分为两类:
{"code":"0","message":"success","data":{}}
{"code":"0","message":"success","data":[]}
我们真正需要的data
所包含的数据,而code
只使用一次,message
则几乎不用。如果Gson不支持泛型或不知道Gson支持泛型的同学一定会这么定义POJO。
public class UserResponse {
public int code;
public String message;
public User data;
}
当其它接口的时候又重新定义一个XXResponse
将data
的类型改成XX,很明显code
,和message
被重复定义了多次,通过泛型的话我们可以将code
和message
字段抽取到一个Result
的类中,这样我们只需要编写data
字段所对应的POJO即可,更专注于我们的业务逻辑。如:
public class Result<T> {
public int code;
public String message;
public T data;
}
// 使用
// 不再重复定义Result类
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;
那么对于data
字段是User
时则可以写为 Result
,当是个列表的时候为 Result>
,其它同理。
对于泛型封装可以参考: 《搞定Gson泛型封装》
自定义解析
由于js和java的命名方式不同,如js采用下划线_
连接命名而java采用驼峰命名,此时前后端的字段无法对应,导致映射失败,可以采用自定义解析方式,规定规则,或者采用注解方式。
GsonBuilder
定制gson实例对象中的解析方式
Gson gson = new GsonBuilder()
.registerTypeAdapter(Id.class, new IdTypeAdapter())
.enableComplexMapKeySerialization()
.serializeNulls()
.setDateFormat(DateFormat.LONG)
.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)//会把字段首字母大写
.setPrettyPrinting()
.setVersion(1.0)
.create();
此处我们关注setFieldNamingPolicy
方法即可,传入解析命名规则:
Enum Constant | Description |
---|---|
IDENTITY | 原样解析 |
LOWER_CASE_WITH_DASHES | 将Java驼峰规则转换为小写用 - 连接的字段来映射 |
LOWER_CASE_WITH_DOTS | 将Java驼峰规则转换为小写用 . 连接的字段来映射 |
LOWER_CASE_WITH_UNDERSCORES | 将Java驼峰规则转换为小写用 _ 连接的字段来映射 |
UPPER_CAMEL_CASE | 将Java驼峰规则转换为首字母也大写的规则 |
UPPER_CAMEL_CASE_WITH_SPACES | 将Java驼峰规则转换为首字母也大写,且使用空格分割 |
其余属性均可在该网站查询到:
- setFieldNamingPolicy 设置序列字段的命名策略(UPPER_CAMEL_CASE,UPPER_CAMEL_CASE_WITH_SPACES,LOWER_CASE_WITH_UNDERSCORES,LOWER_CASE_WITH_DASHES)
- addDeserializationExclusionStrategy 设置反序列化时字段采用策略ExclusionStrategy,如反序列化时不要某字段,当然可以采用@Expore代替。
- excludeFieldsWithoutExposeAnnotation 设置没有@Expore则不序列化和反序列化
- addSerializationExclusionStrategy 设置序列化时字段采用策略,如序列化时不要某字段,当然可以采用@Expore代替。
- registerTypeAdapter 为某特定对象设置固定的序列和反序列方式,实现JsonSerializer和JsonDeserializer接口
- setFieldNamingStrategy 设置字段序列和反序列时名称显示,也可以通过@Serializer代替
- setPrettyPrinting 设置gson转换后的字符串为一个比较好看的字符串
- setDateFormat 设置默认Date解析时对应的format格式
@Expose注解
如果采用new Gson()方式创建Gson则@Expose则没有任何效果,若采用GsonBuilder创建Gson并且调用了excludeFieldsWithoutExposeAnnotation则@Expose将会影响toJson和fromGson序列化和反序列化数据。
public class User {
@Expose private String firstName;
@Expose(serialize = false) private String lastName;
@Expose(serialize = false, deserialize = false) private String emailAddress;
private String password;
}
例子中password不管是toJson还是fromJson都不会用到,emailAddress和lastName在序列化(fromJson)时将被采用,emailAddress在反序列化(toJson)时将不被采用。
@SerializedName注解
该注解标注在JavaBean的字段上,用于候选对应解析的字段
@SerializedName 注解解决 JSON 字符串键名称和 POJO 类属性名不对应的问题
// 如果json字符串为
String jsonStr="{'my_name':'pp'}";
// JavaBean中name字段为
@SerializedName("my_name")
private String myName;
SerializedName
注解提供了两个属性,上面用到了其中一个,别外还有一个属性alternate
,接收一个String数组。
注:alternate
需要2.4版本
@SerializedName(value = "emailAddress", alternate = {"email", "email_address"})
public String emailAddress;
注意:当上面的三个属性(email_address、email、emailAddress)都中出现任意一个时均可以得到正确的结果。
注:当多种情况同时出时,以最后一个出现的值为准。
解析复杂对象
参考:https://www.jianshu.com/p/185e1ee9f05b
本文参考资料:
https://www.jianshu.com/p/31396863d1aa
https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/FieldNamingPolicy.html
https://www.jianshu.com/p/558844c96fc1
https://github.com/google/gson/blob/master/UserGuide.md
https://www.jianshu.com/p/e740196225a4