1. Gson 入门
我们已经接触过 Jackson,并了解了为什么它被视为 Java 处理中 JSON 的事实标准。但还有另一款在社区中、尤其在 Android 领域非常流行的库——Gson。Gson 由 Google 开发,作为将 Java 对象与 JSON 进行序列化与反序列化的轻量、易用方案。它以极低的上手门槛著称:几乎无需配置即可开始使用——大多数需求都能“开箱即用”地解决。
Gson 的另一大优势在于轻量。它体积小,不会引入一大堆依赖,因此在对应用体积敏感的场景(例如移动端)常被采用。Gson 实际上已成为 Android 项目的事实标准——在那里的确是小巧与简洁更为重要。
顺便说一下,Gson 的名称来自 Google JSON。社区里有时还会有个玩笑式的解读——Genius’ Son(“天才之子”),当然这并不官方,只是个文字梗。
将 Gson 引入项目
如果你使用 Maven 或 Gradle,只需添加依赖(版本可能不同):
Maven:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
Gradle:
implementation 'com.google.code.gson:gson:2.10.1'
我们暂时还没有学习项目构建,因此起步阶段可以直接从 Gson 官方页面 下载 jar 文件并将其添加到项目中。
2. 基本操作:序列化与反序列化
让我们用一个简单的类,演示如何使用 Gson 进行对象的序列化与反序列化。
示例:User 类
// 示例用类
public class User {
private String name;
private int age;
private boolean active;
// 构造函数
public User(String name, int age, boolean active) {
this.name = name;
this.age = age;
this.active = active;
}
// getter 和 setter(必要时 Gson 会使用它们)
public String getName() { return name; }
public int getAge() { return age; }
public boolean isActive() { return active; }
}
序列化:对象 → JSON
import com.google.gson.Gson;
public class GsonExample {
public static void main(String[] args) {
User user = new User("Alice", 25, true);
Gson gson = new Gson();
String json = gson.toJson(user);
System.out.println(json);
// {"name":"Alice","age":25,"active":true}
}
}
请注意:字段会按类中的名称进行序列化!
反序列化:JSON → 对象
public class GsonExample {
public static void main(String[] args) {
String json = "{\"name\":\"Bob\",\"age\":30,\"active\":false}";
Gson gson = new Gson();
User user = gson.fromJson(json, User.class);
System.out.println(user.getName()); // Bob
System.out.println(user.getAge()); // 30
System.out.println(user.isActive());// false
}
}
处理对象列表
Gson 在集合处理上比 Jackson 稍微复杂一些,但完全可解决。
import java.util.List;
import java.util.Arrays;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
public class GsonListExample {
public static void main(String[] args) {
List<User> users = Arrays.asList(
new User("Alice", 25, true),
new User("Bob", 30, false)
);
Gson gson = new Gson();
String json = gson.toJson(users);
System.out.println(json);
// [{"name":"Alice","age":25,"active":true},{"name":"Bob","age":30,"active":false}]
// 反序列化列表
Type userListType = new TypeToken<List<User>>(){}.getType();
List<User> users2 = gson.fromJson(json, userListType);
System.out.println(users2.get(0).getName()); // Alice
}
}
重要提示:反序列化集合请使用 TypeToken<>!
3. 配置 Gson:GsonBuilder
Gson 通过 GsonBuilder 提供灵活的配置。你可以启用美化输出、序列化 null、设置日期格式等。
示例:美化输出与序列化 null
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class GsonBuilderExample {
public static void main(String[] args) {
User user = new User("Charlie", 0, false);
Gson gson = new GsonBuilder()
.setPrettyPrinting() // 美化输出(缩进)
.serializeNulls() // 序列化为 null 的字段
.create();
String json = gson.toJson(user);
System.out.println(json);
/*
{
"name": "Charlie",
"age": 0,
"active": false
}
*/
}
}
日期格式化
如果你有 Date 类型的字段,默认 Gson 会使用特定格式进行序列化。你可以自定义格式:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.Date;
public class DateExample {
private String event;
private Date date;
public DateExample(String event, Date date) {
this.event = event;
this.date = date;
}
}
public class Main {
public static void main(String[] args) {
DateExample meeting = new DateExample("Team Meeting", new Date());
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.create();
String json = gson.toJson(meeting);
System.out.println(json);
// {"event":"Team Meeting","date":"2024-06-10 13:45:23"}
}
}
4. Gson 注解:控制序列化
Gson 支持注解,以便更精细地控制序列化与反序列化。
@SerializedName — 字段重命名
如果你希望 JSON 中的字段名与类中的不同,请使用 @SerializedName:
import com.google.gson.annotations.SerializedName;
public class User {
@SerializedName("full_name")
private String name;
private int age;
private boolean active;
public User(String name, int age, boolean active) {
this.name = name;
this.age = age;
this.active = active;
}
}
现在序列化时,该字段名将是 full_name:
User user = new User("Diana", 28, true);
String json = new Gson().toJson(user);
// {"full_name":"Diana","age":28,"active":true}
@Expose — 仅序列化被标注的字段
如果你只想序列化特定字段,请使用 @Expose,并相应配置 Gson:
import com.google.gson.annotations.Expose;
public class User {
@Expose
private String name;
@Expose
private int age;
private boolean active; // 不会被序列化
public User(String name, int age, boolean active) {
this.name = name;
this.age = age;
this.active = active;
}
}
创建支持 @Expose 的 Gson:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
User user = new User("Eve", 21, false);
String json = gson.toJson(user);
// {"name":"Eve","age":21}
@Since/@Until — 按版本进行条件序列化
可以使用 @Since 和 @Until 让字段只在特定版本中参与序列化(实务中不常用,但了解有益)。
5. Gson 的特性与限制
处理嵌套对象
Gson 能很好地处理嵌套对象:
public class Profile {
private User user;
private String bio;
public Profile(User user, String bio) {
this.user = user;
this.bio = bio;
}
}
Profile profile = new Profile(new User("Frank", 27, true), "Java developer");
String json = new Gson().toJson(profile);
// {"user":{"name":"Frank","age":27,"active":true},"bio":"Java developer"}
处理集合
序列化集合(List、Map)没有问题,但反序列化时请使用 TypeToken(见上文)。
与 Jackson 相比的限制
- (较早版本)不支持 Java record 类
- 对新的日期时间 API 支持有限(例如 LocalDate、LocalDateTime——需要自定义适配器)
- 不能使用 Jackson 的注解
- 不开箱即用地支持复杂的多态结构
- 不支持双向引用(循环)的自动处理
自定义适配器(TypeAdapter)
如果标准功能不够用,可以为复杂类型编写自定义的序列化/反序列化适配器。
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
public class BooleanAsIntAdapter extends TypeAdapter<Boolean> {
@Override
public void write(JsonWriter out, Boolean value) throws IOException {
out.value(value ? 1 : 0);
}
@Override
public Boolean read(JsonReader in) throws IOException {
return in.nextInt() == 1;
}
}
// 用法:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Boolean.class, new BooleanAsIntAdapter())
.create();
6. 实践:带配置的序列化与反序列化
让我们扩展你的练习应用,添加以 JSON 格式保存与加载用户列表的功能。
带注解的 User 类
import com.google.gson.annotations.SerializedName;
import com.google.gson.annotations.Expose;
public class User {
@Expose
@SerializedName("full_name")
private String name;
@Expose
private int age;
private boolean active; // 不会被序列化
public User(String name, int age, boolean active) {
this.name = name;
this.age = age;
this.active = active;
}
// getter、setter...
}
将用户列表保存为 JSON
import java.util.List;
import java.util.Arrays;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class SaveUsers {
public static void main(String[] args) {
List<User> users = Arrays.asList(
new User("Ivan", 23, true),
new User("Olga", 19, false)
);
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.setPrettyPrinting()
.create();
String json = gson.toJson(users);
System.out.println(json);
/*
[
{
"full_name": "Ivan",
"age": 23
},
{
"full_name": "Olga",
"age": 19
}
]
*/
}
}
从 JSON 加载用户列表
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;
public class LoadUsers {
public static void main(String[] args) {
String json = "[{\"full_name\":\"Ivan\",\"age\":23},{\"full_name\":\"Olga\",\"age\":19}]";
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
Type userListType = new TypeToken<List<User>>(){}.getType();
List<User> users = gson.fromJson(json, userListType);
for (User user : users) {
System.out.println(user.getName() + " (" + user.getAge() + ")");
}
// Ivan (23)
// Olga (19)
}
}
7. Gson 与 Jackson 的比较
| 特性 | Gson | Jackson |
|---|---|---|
| 易用性 | +++++(非常简单) | +++(稍复杂) |
| 库大小 | 小 | 更大 |
| 速度 | 快,但略慢 | 非常快 |
| 灵活性 | 中等 | 高(可配置项更多) |
| 注解支持 | 自有(@SerializedName) | 自有(@JsonProperty 等) |
| 新类型支持 | 有限 | 出色(Java 8+,record) |
| Android 支持 | 出色 | 良好但更重 |
| 日期处理 | 仅通过适配器 | 开箱即用 |
| 多态 | 受限 | 可灵活配置 |
8. 使用 Gson 时的常见错误
错误 1:未使用 TypeToken 处理集合。
如果要反序列化列表或映射,务必使用 TypeToken<>,否则可能出现奇怪的错误或得到空集合。
错误 2:缺少无参构造函数。
Gson 即使没有默认构造函数也能工作,但在反序列化复杂对象时,缺少无参构造函数有时会导致问题。如果计划进行反序列化,最好始终提供一个无参构造函数。
错误 3:字段名不匹配。
如果 JSON 中的字段名是 "full_name",而类中是 "name",没有 @SerializedName("full_name") 注解就不会绑定,结果将是 null。
错误 4:private 字段问题。
Gson 可以序列化 private 字段,但如果类中只有 private 字段且没有 getter/setter,反序列化时有时会出现问题。最好提供 getter 和 setter。
错误 5:日期处理。
默认情况下,Gson 将 Date 序列化为不太方便的格式。对于 LocalDate、LocalDateTime 等新类型,如果没有自定义适配器,会出现序列化问题。
错误 6:启用了 excludeFieldsWithoutExposeAnnotation() 却未使用 @Expose。
如果你启用了 excludeFieldsWithoutExposeAnnotation(),但未给字段加上 @Expose 注解,这些字段将不会被序列化/反序列化——结果可能是空 JSON 或包含 null 的对象。
GO TO FULL VERSION