Gson — thư viện của Google để tuần tự hóa các đối tượng Java thành JSON và ngược lại, được sử dụng rộng rãi trong phát triển Android. Nó cho phép chuyển đổi các đồ thị đối tượng phức tạp thành chuỗi JSON nhỏ gọn mà không cần viết trình phân tích cú pháp thủ công. Theo Google Gson, 2024, thư viện có hơn 23 nghìn sao trên GitHub và vẫn là một trong những giải pháp phổ biến nhất để làm việc với JSON trong hệ sinh thái Java và Kotlin.
Những Điểm Chính
Gson là thư viện Java do Google phát triển để chuyển đổi đối tượng thành biểu diễn JSON và ngược lại. Nó sử dụng phản chiếu để phân tích cấu trúc lớp, cho phép làm việc mà không cần cấu hình trước. Gson hỗ trợ các đối tượng Java tùy ý, bộ sưu tập, mảng, kiểu generic và lớp lồng nhau. Thư viện không yêu cầu chú thích cho việc sử dụng cơ bản nhưng cung cấp chúng để tinh chỉnh. Nhược điểm chính của phản chiếu là giảm hiệu suất trong quá trình khởi tạo và không thể tối ưu hóa tại thời điểm biên dịch, điều này đặc biệt đáng chú ý khi khởi động nguội ứng dụng Android khi giải tuần tự hóa hàng trăm mô hình. Mặc dù vậy, Gson vẫn là lựa chọn đáng tin cậy cho hầu hết các dự án nhờ tính ổn định và tài liệu phong phú.
Gson được Google phát hành vào năm 2008 và nhanh chóng trở thành tiêu chuẩn thực tế cho JSON trong các ứng dụng Android. Trước khi Moshi và kotlinx.serialization ra đời, Gson là lựa chọn phổ biến duy nhất cho các dự án Kotlin. Dễ dàng tích hợp — chỉ cần thêm một phụ thuộc vào build.gradle — và không có chú thích bắt buộc đã khiến Gson trở nên phổ biến đối với các nhà phát triển ở mọi trình độ.
// Thêm Gson trong build.gradle
dependencies {
implementation 'com.google.code.gson:gson:2.10.1'
}
// Sử dụng cơ bản
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"}
Ngoài tuần tự hóa cơ bản, Gson cung cấp GsonBuilder để cấu hình hành vi: định dạng ngày tháng, tắt thoát HTML, định dạng khóa và các phiên bản tùy chỉnh. GsonBuilder cũng cho phép đăng ký JsonSerializer và JsonDeserializer tùy chỉnh cho các loại mà thư viện không thể xử lý tự động. Tính linh hoạt trong cấu hình làm cho GsonBuilder trở thành công cụ không thể thiếu và hữu ích khi điều chỉnh thư viện theo các yêu cầu cụ thể của dự án trong phát triển Android hiện đại.
toJson chuyển đổi đối tượng Java thành chuỗi JSON bằng cách phân tích các trường của nó thông qua phản chiếu. Theo mặc định, Gson bao gồm tất cả các trường ngoại trừ transient và static. Phương thức này hỗ trợ mọi loại: nguyên thủy, đối tượng, bộ sưu tập và mảng. fromJson thực hiện thao tác ngược lại, chấp nhận chuỗi JSON và lớp đối tượng đích, trả về một phiên bản với các trường được điền đầy.
Trong quá trình tuần tự hóa, Gson duyệt đệ quy tất cả các trường của đối tượng, bao gồm cả các trường lồng nhau. Tham chiếu vòng dẫn đến StackOverflowError, vì vậy chúng phải được loại trừ thông qua chú thích @Expose hoặc bộ chuyển đổi tùy chỉnh. Đối với bộ sưu tập, Gson giữ nguyên loại phần tử, nhưng khi giải tuần tự hóa danh sách với kiểu generic, cần TypeToken để giữ lại thông tin loại.
// data class với đối tượng lồng nhau
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"))
// Tuần tự hóa thành JSON
val json = gson.toJson(employee)
// Giải tuần tự hóa từ JSON
val jsonString = """
{"id":2,"name":"Bob","address":{"city":"London","street":"Baker St"}}
"""
val parsed = gson.fromJson(jsonString, Employee::class.java)
Gson cung cấp một bộ chú thích để quản lý quá trình tuần tự hóa. @SerializedName chỉ định tên khóa JSON khác với tên trường. @Expose kiểm soát việc một trường có được bao gồm trong tuần tự hóa hay không: Gson được tạo qua GsonBuilder.excludeFieldsWithoutExposeAnnotation() sẽ chỉ xử lý các trường có @Expose. @Since và @Until kiểm soát việc quản lý phiên bản trường.
Chú thích @SerializedName giải quyết vấn đề không khớp tên: máy chủ có thể sử dụng snake_case trong khi mã sử dụng camelCase. Chú thích chấp nhận một giá trị và các lựa chọn thay thế tùy chọn để tương thích ngược. @Expose cho phép ẩn các trường nhạy cảm (mật khẩu, mã thông báo) khỏi tuần tự hóa bằng cách đánh dấu chúng là @Expose(serialize = false). Ngoài việc bao gồm và loại trừ, @Expose có thể được kết hợp với GsonBuilder.excludeFieldsWithoutExposeAnnotation để tạo danh sách trắng các trường, giúp kiểm soát bề mặt tấn công khi tuần tự hóa các đối tượng có nhiều trường.
// Mô hình với chú thích 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
)
// Gson với lọc @Expose
val gson = GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.setPrettyPrinting()
.create()
val user = UserResponse(1, "John", "secret123")
println(gson.toJson(user))
// {"user_id":1,"full_name":"John"} — mật khẩu bị loại trừ
Vấn đề với kiểu generic trong Java và Kotlin là xóa loại tại thời điểm biên dịch. Khi Gson giải tuần tự hóa List<User>, nó không biết loại phần tử và trả về List<Map<String, Any>>. Để giữ lại thông tin loại, Gson cung cấp TypeToken — một lớp trừu tượng captures tham số loại thông qua lớp ẩn danh. Nếu không có TypeToken, nhà phát triển phải thủ công chuyển đổi từng phần tử từ Map sang loại đích, dẫn đến mã cồng kềnh và giảm hiệu suất.
TypeToken giải quyết vấn đề xóa loại. Nhà phát triển tạo một lớp con ẩn danh của TypeToken với tham số loại cần thiết và Gson sử dụng thông tin từ chữ ký lớp để giải tuần tự hóa chính xác. TypeToken cũng hoạt động với Map, Set và bất kỳ loại tham số hóa nào khác, bao gồm cả kiểu generic lồng nhau. Đặc biệt, đối với Map<String, List<User>>, cần TypeToken với chữ ký loại lồng nhau đầy đủ, nếu không Gson sẽ giải tuần tự hóa các giá trị dưới dạng List<Map<String, Any>> thay vì List<User>.
// TypeToken để giải tuần tự hóa danh sách
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)
// Bộ giải tuần tự hóa tùy chỉnh
class LocalDateAdapter :
JsonDeserializer<LocalDate> {
override fun deserialize(
json: JsonElement,
typeOfT: java.lang.reflect.Type,
context: JsonDeserializationContext
): LocalDate {
return LocalDate.parse(json.asString)
}
}
Đối với logic tuần tự hóa tùy chỉnh, Gson hỗ trợ các giao diện JsonSerializer và JsonDeserializer. Chúng được đăng ký qua GsonBuilder.registerTypeAdapter() và cho phép xử lý các loại mà thư viện không thể tuần tự hóa tự động: ngày Java 8, Enum với giá trị không chuẩn hoặc các lớp bên thứ ba không có quyền truy cập mã nguồn. Khi triển khai bộ chuyển đổi, điều quan trọng là phải theo dõi hiệu suất: gọi phản chiếu bên trong bộ chuyển đổi tùy chỉnh sẽ vô hiệu hóa lợi thế của kiểm soát thủ công, vì vậy các cuộc gọi trực tiếp đến phương thức và trường được ưu tiên hơn. Trong hệ sinh thái Gson, cũng có mô-đun gson-extras cung cấp các bộ chuyển đổi cho các loại phổ biến như UUID, Optional và bánh xe ngày Joda-Time.
GsonBuilder cung cấp hàng chục phương thức để tinh chỉnh tuần tự hóa. setPrettyPrinting thêm thụt đầu dòng và ngắt dòng vào JSON đầu ra để dễ đọc. disableHtmlEscaping tắt thoát ký tự HTML trong chuỗi. setDateFormat chỉ định định dạng ngày, điều này rất quan trọng khi làm việc với các máy chủ sử dụng biểu diễn thời gian không chuẩn. setLenient bật chế độ phân tích linh hoạt, bỏ qua một số lỗi định dạng JSON. addDeserializationExclusionStrategy cho phép loại trừ các trường khỏi giải tuần tự hóa theo chương trình dựa trên các chiến lược tùy chỉnh. Để gỡ lỗi, setPrettyPrinting kết hợp với ghi nhật ký rất hữu ích — nó làm cho các phản hồi JSON có thể đọc được trong nhật ký và đơn giản hóa việc tìm kiếm sự không khớp.
Một tính năng quan trọng của GsonBuilder là quản lý phiên bản trường thông qua các chú thích @Since và @Until. Nhà phát triển chỉ định phiên bản đối tượng qua setVersion và Gson tự động bao gồm hoặc loại trừ các trường dựa trên chú thích phiên bản của chúng. Điều này hữu ích trong quá trình phát triển API khi cùng một mô hình được sử dụng cho các phiên bản khác nhau của giao thức máy chủ. GsonBuilder cũng hỗ trợ đăng ký TypeAdapterFactory để xử lý toàn cục các loại họ và complexMapKeySerialization để làm việc chính xác với các khóa Map phức tạp.
Câu Hỏi Thường Gặp
Gson là thư viện của Google để chuyển đổi các đối tượng Java thành JSON và ngược lại. Nó được sử dụng rộng rãi trong các ứng dụng Android để phân tích phản hồi máy chủ, tuần tự hóa yêu cầu và lưu trữ dữ liệu trong bộ nhớ cục bộ.
Theo mặc định, Gson bỏ qua các trường null trong quá trình tuần tự hóa. Để bao gồm giá trị null, hãy sử dụng GsonBuilder.serializeNulls(). Trong quá trình giải tuần tự hóa, các trường bị thiếu trong JSON vẫn là null hoặc nhận giá trị mặc định cho loại đó.
Moshi không sử dụng phản chiếu cho các lớp Kotlin, mang lại hiệu suất cao hơn và hành vi có thể dự đoán trước. Moshi cũng xử lý chính xác tính an toàn null của Kotlin, trong khi Gson có thể giải tuần tự hóa null thành trường không null, gây ra ngoại lệ.
@SerializedName liên kết một khóa JSON với trường của lớp khi tên của chúng không khớp. Ví dụ: cho trường kotlinName và khóa JSON "kotlin_name", chú thích @SerializedName("kotlin_name") đảm bảo chuyển đổi chính xác.
TypeToken là một lớp trừu tượng captures tham số loại thông qua lớp ẩn danh. Nó cần thiết để giải tuần tự hóa các bộ sưu tập và các loại tham số hóa khác, vì do xóa loại, Gson không thể khôi phục loại phần tử trong thời gian chạy.
Tổng Kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm