2018-9-5 20:32 - QG2017移动组 - 张艺隽
Gson是使用率非常高的Json解析框架,提供快捷的方法来实现Java实体类和Json字符串的转换。
Gson is a Java library that can be used to convert Java Objects into their JSON representation. It can also be used to convert a JSON string to an equivalent Java object. Gson can work with arbitrary Java objects including pre-existing objects that you do not have source-code of.
There are a few open-source projects that can convert Java objects to JSON. However, most of them require that you place Java annotations in your classes; something that you can not do if you do not have access to the source-code. Most also do not fully support the use of Java Generics. Gson considers both of these as very important design goals.
导入依赖:
implementation 'com.google.code.gson:gson:2.8.5'
使用toJson()方法生成Json字符串。
String userData = new Gson().toJson(user);
val userData = Gson().toJson(user)
使用fromJson()方法将Json字符串转化为Java实体类
- Java:
User user = new Gson().fromJson(userData,User.class);
val user = Gson().fromJson(userData,User::class.java)
在实际情况中,经常因为后台瞎写接口,导致代码与文档不符,这种问题常出现于大小写、下划线。例如:
realpath
realpath字段按照后台提供的接口文档编写,但后台实际代码却是使用了驼峰的realPath。这种情况下使用 @SerializedName重命名注解,将可能发生错误的字段进行重命名处理。如:
- Java
@SerializedName(value = "realpath", alternate = {"realPath","RealPath", "real_path", "Realpath"})
private String realpath;
@SerializedName(value = "realpath", alternate = {"realPath","RealPath", "real_path", "Realpath"})
var realpath : String
在注解中用alternate属性,为realpath提供了几种可能的命名,包括
- 驼峰的realPath
- 单词大写的RealPath
- 首字母大写的Realpath
- js风格的real_path
一些需要注意的点:
{
"realpath": "C:\\a.txt",
"real_path": "E:\\b.jpg",
"realPath": "F:\\c.doc"
}
System.out.println(file.realpath); // F:\c.doc
println(file.realpath)
在实际情况中,有些实体类包含本地字段,这些字段无需加入json中,那么就可以用 @Expose 注解。
@Expose 注解从名字上就可以看出是暴露的意思,所以该注解是用于对外暴露字段的。该注解必须和GsonBuilder配合使用。
使用方法: 简单说来就是需要导出的字段上加上 @Expose 注解,不导出的字段不加。注意是不导出的不加。
@Expose //序列化和反序列化都都生效
@Expose(deserialize = true,serialize = true) //序列化和反序列化都都生效,等价于上一条
@Expose(deserialize = true,serialize = false) //反序列化时生效
@Expose(deserialize = false,serialize = true) //序列化时生效
@Expose(deserialize = false,serialize = false) // 和不写注解一样
String jsonBody = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create()
.toJson(user);
val jsonBody = GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create()
.toJson(user)
Gson提供基于版本的注解:@Since和 @Until 当设置版本大于Since或小于Until的时候将字段导出,例如:
- Java
public class User{
@Since(2)
public String name;
@Until(4)
public int age;
}
String json = new GsonBuilder()
.setVersion(version)
.create()
.toJson(user);
data class(@Since(2)var name:String, @Until(4)var age:Int)
val json = GsonBuilder()
.setVersion(version)
.create()
.toJson(User)
version >= 4 : {“name”: “zhangyijun”}
@Since和@Until注解均用于序列化
Gson提供基于修饰符进行序列化/反序列化的方法:excludeFieldsWithModifiers(),参数由java.lang.reflect.Modifier提供。
- Java
public class User{
private String name = "zyj";
static int age = 18;
final int height = 180;
}
String json = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.FINAL,MOdifier.STATIC)
.create()
.toJson(user);
输出结果:{“name”:”zyj”}
- Kotlin
data class User(private var name: String = "zyj", val age: Int = 18) {
companion object {
var height: Int = 180
}
}
Kotlin中static的值以companion object形式存在,使用Gson的时候默认不会将其序列化,使用excludeFieldsWithModifiers()方法后可以将static的值打印出来
val json = GsonBuilder()
.excludeFieldsWithModifiers()
.create()
.toJson(user)
输出结果:
{
"age": 18,
"name": "zyj",
"Companion": {
"serialVersionUID": -3895247665888796413
},
"height": 180,
"serialVersionUID": 1851844703672024324
}
在Kotlin使用excludeFieldsWithModifiers()方法后,无论是序列化还是反序列化,serialVersionUID都是可读可写的。
private static final long serialVersionUID = 1L;
为了保证程序的安全,在excludeFieldsWithModifiers()方法中至少传入private/static/final的一种,以隐藏serialVersionUID这一属性。
基于策略是一种非常灵活的规则,相比@Expose、@Since、@Until、excludeFieldsWithModifiers()更加强大,但代码量也会少量提升。Gson提供两种设置策略的方法:
- addSerializationExclusionStrategy()
- addDeserializationExclusionStrategy()
分别针对序列化和反序列化,直接看大佬的代码
- Java
Gson gson = new GsonBuilder()
.addSerializationExclusionStrategy(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
// 这里作判断,决定要不要排除该字段,return true为排除
if ("finalField".equals(f.getName())) return true; //按字段名排除
Expose expose = f.getAnnotation(Expose.class);
if (expose != null && expose.deserialize() == false) return true; //按注解排除
return false;
}
@Override
public boolean shouldSkipClass(Class> clazz) {
// 直接排除某个类 ,return true为排除
return (clazz == int.class || clazz == Integer.class);
}
})
.create();
作者:怪盗kidou
链接:https://www.jianshu.com/p/0e40a52c0063
val gson = GsonBuilder()
.addSerializationExclusionStrategy(object : ExclusionStrategy {
override fun shouldSkipField(f: FieldAttributes): Boolean {
if ("finalField" == f.name) return true
val expose = f.getAnnotation(Expose::class.java)
return expose != null && !expose.deserialize
}
override fun shouldSkipClass(clazz: Class<*>): Boolean {
return clazz == Int::class.javaPrimitiveType || clazz == Int::class.java
}
})
.create()
GsonBuilder提供两种方法让我们自定义映射规则:
GsonBuilder的setFieldNamingPolicy() 方法,参数为Gson提供的FieldNamingPolicy枚举类。
- Java
Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)
.create();
val gson = GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)
.create()
FieldNamingPolicy的参数有:
FieldNamingPolicy | 结果(仅输出emailAddress字段) |
---|---|
IDENTITY | {“emailAddress”:”[email protected]”} |
LOWER_CASE_WITH_DASHES | {“email-address”:”[email protected]”} |
LOWER_CASE_WITH_UNDERSCORES | {“email_address”:”[email protected]”} |
UPPER_CAMEL_CASE | {“EmailAddress”:”[email protected]”} |
UPPER_CAMEL_CASE_WITH_SPACES | {“Email Address”:”[email protected]”} |
- 有减号、下划线、驼峰、空格等规则。
GsonBuilder的setFieldNamingStrategy() 方法,参数为Gson提供的FieldNamingStrategy接口
ctrl+c加ctrl+v:
Gson gson = new GsonBuilder()
.setFieldNamingStrategy(new FieldNamingStrategy() {
@Override
public String translateName(Field f) {
//实现自己的规则
return null;
}
})
.create();
作者:怪盗kidou
链接:https://www.jianshu.com/p/0e40a52c0063
val gson = GsonBuilder()
.setFieldNamingStrategy { f -> f?.name }
.create()
假设有一返回数据类:
- Java
public class UserResponse {
public int code;
public String message;
public User data;
}
data class UserResponse(var code:Int, var message:String, var data:User)
在编写实体类的时候,发现data不仅是User,也有可能是List,甚至是其他类或基本类型。这时可能要编写十几个实体类,十分麻烦。
以下为Json数据
{
"code": 200,
"message": "success",
"data": {
"user": {
"name": "zhangyijun",
"num": 3117004910
}
}
}
引入泛型前,需要额外编写UserResult和UserListResult两种实体类
- Java
String json = "{..........}";
UserResult userResult = new Gson().fromJson(json,UserResult.class);
User user = userResult.data;
UserListResult userListResult = new Gson().fromJson(json,UserListResult.class);
List<User> users = userListResult.data;
val json = "{..........}"
var user = Gson().fromJson(json,UserResult::class.java)
var users = Gson().fromJson(json,UserListResult::class.java)
引入泛型后只需使用统一标准格式的返回数据类即可
- Java
public class Result{
public int code;
public String message;
public T data;
}
String json = "{..........}";
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 class Result<T>(var code: Int, var message: String, var data: T)
var userType = TypeToken<Result<User>>(){}.getType()
var user = Gson()
.fromJson(json,userType)
.data
var userListType = TypeToken<Result<List<User>>>(){}.getType()
var users = Gson()
.fromJson(json,userListType)
.data
在使用Gson时,默认不会导出null的值和键,当我们需要导出null值的时就需要使用GsonBuilder
Gson gson = new GsonBuilder()
.serializeNulls()
.create();
val gson = GsonBuilder()
.serializeNulls()
.create()
val gson = GsonBuilder()
.setDateFormat("yyyy-MM-dd")
.create()
val gson = GsonBuilder()
.setPrettyPrinting()
.create()
结语:花了一晚上的时间复习Gson,总感觉有很多知识点都是用不上的,而且看完之后也很容易忘掉。但是花时间去琢磨一下也是挺不错的。Gson就像其他主流框架一样,经过十多年的发展,已经变得十分完善。这篇学习笔记中,Gson还有很多方法没有提及到,包括非常灵活但是很麻烦的TypeAdapter(用来接管某种类型的序列化和反序列化过程)。在阶段性(且碎片化)的学习中,掌握一种主流框架的50%已经是成功了,剩下的50%作为提高,可以阅读源码,这肯定会花费大量时间。考虑到搭建属于自己的技术堆栈,我可能会学习阿里出品的FastJson。
#Gson - 完()