Gson是一个相对简单的库,没有那么多功能,从设计上也并不想让别人去扩展它,它只想安安静静地做一个Json序列化库,简单而实用。
简单说
Gson提供两种方式创建Gson实例
new Gson()
:快速创建,默认配置,快速使用new GsonBuilder().setxxxxx().create()
:完整方式创建,支持一些自定义化的配置
Gson突出一个简单,API如此,功能更是如此,大致列一下其支持的功能
- 基于field的序列化与反序列化:基本特性
- 支持自定义属性名:@SerializedName
- 支持反序列化时指定泛型信息:TypeToken
- 支持排除某个字段:transient关键字排除单个字段、按照可见性修饰符排除、@Expose主动选择
- 支持自定义序列化和反序列化逻辑:JsonSerializer、JsonDeserializer,或者它们的集合体:TypeAdapter
基础能力
什么注释也不用加,啥也不用干,直接就能使用
class Resource1<T> {
var id: Int = -1
var type: ResourceType? = null
@Transient
var secret: String = ""
@SerializedName("我是实际数据")
var data: T? = null
override fun toString(): String {
return "Resource1(id=$id, type=$type, secret='$secret', data=$data)"
}
}
fun main() {
val gson = Gson()
val resource = Resource1<JsonObject>().apply {
this.id = 1
this.type = null
this.secret = "我是密码"
this.data = JsonObject().apply {
addProperty("key", "value")
}
}
val jsonString = gson.toJson(resource)
println(jsonString)
println(gson.fromJson<Resource1<JsonObject>>(jsonString, object : TypeToken<Resource1<JsonObject>>() {
}.type))
}
要点
- 序列化用
gson.toJson(xxx)
,反序列化用gson.fromJson(jsonString, 类型信息)
- 忽略字段可以用transient关键字
- 自定义字段名用@SerializedName注解,实际上这是Gson能够个性化配置的唯四之一
- 对于泛型擦除的情况,在反序列化时通过TypeToken指定:
object : TypeToken<Resource1<JsonObject>>() {}.type
。这一点和Jackson和Fastjson的TypeReference类似
排除字段
class Resource4 {
@Expose
var id: Int = -1
var type: ResourceType? = null
}
fun main() {
val gson = GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()
println(gson.toJson(Resource4().apply {
id = 10; type = ResourceType.CHARACTER }))
}
GsonBuilder().excludeFieldsWithoutExposeAnnotation()
设置只暴露@Expose的数据- @Expose标记字段
自定义实例创建器
Gson反序列化时创建对象的逻辑
- 首先寻找是否存在目标类的无参构造函数,有则用它创建实例
- 其次寻找是否存在用户自定义的实例创建器
- 然后如果目标类是原生类型,则直接查找对应类型的构造器并创建实例
- 都没有,则用sun.misc.Unsafe创建实例
一般不推荐使用Unsafe创建实例,要么提供无参构造方法,要么提供实例创建器,这里有一个后者的例子
class Resource3(var id: Int, var type: ResourceType?) {
init {
println("有参构造函数被执行了")
}
override fun toString(): String {
return "Resource3(id=$id, type=$type)"
}
}
class Resource3Creator : InstanceCreator<Resource3> {
override fun createInstance(type: Type): Resource3 {
return Resource3(-1, null)
}
}
fun main() {
val gson = GsonBuilder().registerTypeAdapter(Resource3::class.java, Resource3Creator()).create()
val resource = Resource3(1, null)
println("序列化")
val jsonString = gson.toJson(resource)
println(jsonString)
println("反序列化")
println(gson.fromJson(jsonString, Resource3::class.java))
}
输出
有参构造函数被执行了
序列化
{
"id":1}
反序列化
有参构造函数被执行了
Resource3(id=1, type=null)
PS:Gson默认忽略掉内部类,因为它没有无参构造函数
自定义序列化器
老规矩,自定义LocalDateTime的序列化和反序列化逻辑,为此Gson提供了三种类型可供定义,要么定义序列化器、要么反序列化器,要么同时有。
class Resource5 {
var id: Int = -1
var type: ResourceType = ResourceType.CHARACTER
@JsonAdapter(Resource5TypeAdapter::class)
var updatedTime: LocalDateTime? = null
override fun toString(): String {
return "Resource5(id=$id, type=$type, updatedTime=$updatedTime)"
}
}
class Resource5TypeAdapter : TypeAdapter<LocalDateTime>() {
override fun write(out: JsonWriter, value: LocalDateTime?) {
println("执行了写方法")
if (value == null) out.nullValue()
else out.value(value.toInstant(ZoneOffset.UTC).toEpochMilli())
}
override fun read(`in`: JsonReader): LocalDateTime {
println("执行了读方法")
return LocalDateTime.ofInstant(Instant.ofEpochMilli(`in`.nextLong()), ZoneOffset.UTC)
}
}
fun main() {
val resource = Resource5().apply {
this.id = 1
this.updatedTime = LocalDateTime.now()
}
val gson = Gson()
val jsonString = gson.toJson(resource)
println(jsonString)
println(gson.fromJson(jsonString, Resource5::class.java))
}
- 可通过@JsonAdapter局部指定
- 也可通过
GsonBuilder().registerTypeAdapter
全局注册
多态
Gson原生不支持多态,但可通过一些其它方式实现,以下是官方推荐的方式(尽管很傻)
fun main() {
val gson = Gson()
val list = listOf(
1,
"",
mapOf(
"key" to "value"
)
)
val jsonString = gson.toJson(list)
println(jsonString)
gson.fromJson(jsonString, JsonArray::class.java).mapIndexed {
index, jsonElement ->
when (index) {
0 -> gson.fromJson(jsonElement, Int::class.java)
1 -> gson.fromJson(jsonElement, String::class.java)
2 -> gson.fromJson(jsonElement, object : TypeToken<Map<String, String>>() {
}.type)
else -> throw Exception()
}
}.toList().also {
println(it) }
}
- 如果一个集合中有多重类型,反序列化时,先得到JsonArray,再针对具体元素应用具体类型
- 这就不咋科学,还有一种方式是RuntimeTypeAdapterFactory,这非官方推荐的方式,所以要用多态还是别用Gson了
树模型
Gson的树模型还是简单的,只有JsonArray、JsonObject以及JsonElement三个类,但是API不大友好,限制的比较死
- 添加一般属性就要调用addProperty方法,且只支持String、Boolean、Number、Character四种类型
- 添加对象或数组属性就得用add方法
- 不支持fluent API
fun main() {
val resource = JsonObject().apply {
addProperty("id", 1)
addProperty("type", ResourceType.CHARACTER.name)
addProperty("usn", null as String?)
add("data", JsonArray().apply {
add(false)
add(123)
})
}
println(GsonBuilder().setPrettyPrinting().create().toJson(resource))
}
支持设置啥能力
穷举一下GsonBuilder,有啥能力,可以看到,其实没啥能力。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-na27YDhi-1635758683237)(https://gdz.oss-cn-shenzhen.aliyuncs.com/local/image-20211016115638338.png)]
- 设置序列化和反序列化的排除策略
- 设置各种类型适配器,用于控制类型序列化和反序列化时的行为
- 关闭内部类的序列化
- Html格式转义
- 序列化名称控制
- 输出格式化
- 版本控制(@Since和@Until注解可设置POJO的版本,有点类似@JsonView的功能,但感觉非常鸡肋)
- 设置日期格式
- 设置字段名命名策略
基本原理
加上Gson,前前后后看了五个序列化库,除了Java,原理结构上都大同小异,只是在序列化和反序列化的具体算法上有所差别,扣得比较细节,尤其是Fastjson用了很多奇技淫巧,硬是把速度提了上去。
- 对于序列化,首先得到序列化器,再用序列化器将实际对象写入流
- 对反序列化,首先得到反序列化器,再得到目标类的实例,再用反序列化器从流中读取内容塞入目标实例
至于Gson,它比较与众不同的点在于
- 序列化时,直接使用了StringWriter做写操作,而不是自己维护输出流和缓冲区
- Json格式的实际写入在com.google.gson.stream.JsonWriter,内部套StringWriter
- 写入状态控制与Jackson采用树状结构不同,它采用了栈(一个一维数组,数组的大小即当前层次结构的深度,数组的值即当前所处结构的类型)的方式维护,参见com.google.gson.stream.JsonWriter#stack
- 对未自定义序列化器的类型来说,使用com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.Adapter达成
- 对于序列化,它直接使用了反射获取符合要求的字段,然后写入writer
- 对于反序列化,它使用前文"自定义实例创建器"所说的方式创建对象,然后通过反射写入目标对象
总结
通篇看起来,Gson定位清晰,目标明确,文档和代码规范,用起来也比较轻松。只是功能简单,原理也简单,直接使用StringWriter和反射,就是一个功能性的Json库,看不大出有什么性能优化,因此可以推测,Gson的性能不会太出色。