Gson — 来自 Google 的库,用于将 Java 对象序列化为 JSON 以及反向转换,广泛用于 Android 开发。它可以将复杂的对象图转换为紧凑的 JSON 字符串,无需手动编写解析器。根据 Google Gson, 2024 的数据,该库在 GitHub 上拥有超过 23,000 颗星,并且仍然是 Java 和 Kotlin 生态系统中处理 JSON 的最流行解决方案之一。
要点
Gson — 是由 Google 开发的 Java 库,用于将对象转换为 JSON 表示形式以及反向转换。它使用反射来分析类的结构,从而无需预先配置即可工作。Gson 支持任意 Java 对象、集合、数组、泛型和嵌套类。该库基本使用不需要注解,但提供了注解以实现精细调整。反射的主要缺点是在初始化时性能下降以及在编译阶段无法优化,这在 Android 应用程序冷启动时反序列化数百个模型时尤其明显。尽管如此,Gson 凭借其稳定性和广泛的文档,仍然是大多数项目的可靠选择。
Gson 由 Google 于 2008 年发布,并迅速成为 Android 应用程序中 JSON 的事实标准。在 Moshi 和 kotlinx.serialization 出现之前,Gson 一直是 Kotlin 项目唯一流行的选择。连接简单 — 在 build.gradle 中添加一个依赖项 — 且没有强制注解,使得 Gson 在各个级别的开发人员中都很受欢迎。
// 在 build.gradle 中添加 Gson
dependencies {
implementation 'com.google.code.gson:gson:2.10.1'
}
// 基本用法
data class User(
val id: Int,
val name: String,
val email: String
)
val gson = Gson()
val user = User(1, "John", "john@test.com")
val json = gson.toJson(user)
println(json) // {"id":1,"name":"John","email":"john@test.com"}
除了基本序列化之外,Gson 还提供 GsonBuilder 用于配置行为:日期格式化、禁用 HTML 转义、键注册和自定义实例。GsonBuilder 还允许为库无法自动处理的类型注册自定义 JsonSerializer 和 JsonDeserializer。配置的灵活性使 GsonBuilder 成为在现代 Android 开发中将库适配到项目特定要求时不可或缺且有用的工具。
toJson 通过反射分析其字段将 Java 对象转换为 JSON 字符串。默认情况下,Gson 包含除 transient 和 static 之外的所有字段。该方法支持所有类型:基本类型、对象、集合和数组。fromJson 执行反向转换,接收 JSON 字符串和目标对象的类,并返回一个字段已填充的实例。
在序列化期间,Gson 递归遍历对象的所有字段,包括嵌套字段。循环引用会导致 StackOverflowError,因此必须通过 @Expose 注解或自定义适配器排除。对于集合,Gson 保留元素的类型,但在对带有泛型的列表进行反序列化时,需要 TypeToken 来保留类型信息。
// 带有嵌套对象的 data class
data class Address(
val city: String,
val street: String
)
data class Employee(
val id: Int,
val name: String,
val address: Address
)
val gson = Gson()
val employee = Employee(1, "Alice",
Address("New York", "5th Ave"))
// 序列化为 JSON
val json = gson.toJson(employee)
// 从 JSON 反序列化
val jsonString = """
{"id":2,"name":"Bob","address":{"city":"London","street":"Baker St"}}
"""
val parsed = gson.fromJson(jsonString, Employee::class.java)
Gson 提供了一组用于管理序列化过程的注解。@SerializedName 指定与字段名称不同的 JSON 键名称。@Expose 管理字段是否包含在序列化中:通过 GsonBuilder.excludeFieldsWithoutExposeAnnotation() 创建的 Gson 将只处理带有 @Expose 的字段。@Since 和 @Until 控制字段的版本管理。
@SerializedName 注解解决了名称不匹配的问题:服务器可能使用 snake_case,而代码中采用 camelCase。该注解接受一个值和可选的备选方案以实现向后兼容。@Expose 允许通过将敏感字段(密码、令牌)标记为 @Expose(serialize = false) 来将其从序列化中隐藏。除了包含和排除之外,@Expose 可以与 GsonBuilder.excludeFieldsWithoutExposeAnnotation 结合使用,创建字段的白名单,这有助于在序列化具有大量字段的对象时控制攻击面。
// 带有 Gson 注解的模型
data class UserResponse(
@SerializedName("user_id")
val userId: Int,
@SerializedName("full_name",
alternate = [Alternative("name")])
val fullName: String,
@Expose(serialize = false)
val password: String
)
// 带有 @Expose 过滤的 Gson
val gson = GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.setPrettyPrinting()
.create()
val user = UserResponse(1, "John", "secret123")
println(gson.toJson(user))
// {"user_id":1,"full_name":"John"} — password excluded
泛型的问题 在于 Java 和 Kotlin 中编译期间的类型擦除。当 Gson 反序列化 List<User> 时,它不知道元素的类型并返回 List<Map<String, Any>>。为了保留类型信息,Gson 提供了 TypeToken — 一个通过匿名类捕获类型参数的抽象类。如果没有 TypeToken,开发人员必须手动将每个元素从 Map 转换为目标类型,这会导致代码冗长和性能损失。
TypeToken 解决了类型擦除的问题。开发人员创建一个具有所需类型参数的匿名 TypeToken 子类,Gson 使用类签名中的信息进行正确的反序列化。TypeToken 也适用于 Map、Set 和任何其他参数化类型,包括嵌套的泛型。特别是,对于 Map<String, List<User>>,需要具有完整嵌套类型签名的 TypeToken,否则 Gson 会将值反序列化为 List<Map<String, Any>> 而不是 List<User>。
// 用于列表反序列化的 TypeToken
data class Product(
val id: Int,
val title: String,
val price: Double
)
val jsonArray = """
[
{"id":1,"title":"Phone","price":599.0},
{"id":2,"title":"Laptop","price":1299.0}
]
"""
val gson = Gson()
val listType = object : TypeToken<List<Product>>() {}
val products: List<Product> =
gson.fromJson(jsonArray, listType.type)
// 自定义反序列化器
class LocalDateAdapter :
JsonDeserializer<LocalDate> {
override fun deserialize(
json: JsonElement,
typeOfT: java.lang.reflect.Type,
context: JsonDeserializationContext
): LocalDate {
return LocalDate.parse(json.asString)
}
}
对于自定义序列化逻辑,Gson 支持 JsonSerializer 和 JsonDeserializer 接口。它们通过 GsonBuilder.registerTypeAdapter() 注册,允许处理库无法自动序列化的类型:Java 8 日期、具有非标准值的 Enum 或无法访问源代码的第三方类。实现适配器时,监控性能很重要:在自定义适配器内部调用反射会抵消手动管理的优势,因此首选直接的方法和字段调用。Gson 生态系统中还有 gson-extras 模块,它为常见类型提供适配器,例如 UUID、Optional 和 Joda-Time 日期类型。
GsonBuilder 提供了数十种用于精细调整序列化的方法。setPrettyPrinting 为输出 JSON 添加缩进和换行以提高可读性。disableHtmlEscaping 禁用字符串中 HTML 字符的转义。setDateFormat 设置日期格式,这对于使用非标准时间表示的服务器至关重要。setLenient 启用宽松的解析模式,忽略某些 JSON 格式错误。addDeserializationExclusionStrategy 允许基于自定义策略以编程方式从反序列化中排除字段。对于调试,setPrettyPrinting 方法结合日志记录非常有用 — 它使 JSON 响应在日志中可读,并简化了不一致性的查找。
GsonBuilder 的一个重要功能是通过 @Since 和 @Until 注解管理字段版本控制。开发人员通过 setVersion 指定对象的版本,Gson 根据字段的版本注解自动包含或排除字段。这在 API 演进中很有用,当同一个模型用于不同版本的服务器协议时。GsonBuilder 还支持注册 TypeAdapterFactory 以全局处理类型系列,以及 complexMapKeySerialization 以正确处理复杂的 Map 键。
常见问题
Gson — 是 Google 用于将 Java 对象转换为 JSON 以及反向转换的库。它广泛用于 Android 应用程序中解析服务器响应、序列化请求和将数据保存到本地存储。
默认情况下,Gson 在序列化时会跳过为 null 的字段。要启用 null 值,请使用 GsonBuilder.serializeNulls()。在反序列化时,JSON 中缺少的字段保持为 null 或采用该类型的默认值。
Moshi 不为 Kotlin 类使用反射,这提供了更高的性能和可预测的行为。Moshi 也正确处理 Kotlin 的空安全,而 Gson 可以将 null 反序列化到非空字段,导致异常。
@SerializedName 在名称不匹配时将 JSON 键绑定到类的字段。例如,对于字段 kotlinName 和 JSON 键 “kotlin_name”,注解 @SerializedName(“kotlin_name”) 确保正确的转换。
TypeToken — 是一个通过匿名类捕获类型参数的抽象类。它对于反序列化集合和其他参数化类型是必需的,因为由于类型擦除,Gson 无法在运行时恢复元素的类型。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。