1. 引言
Gson 是 Google 开发的开源 Java 库,专门用于对象与 JSON 之间的转换。它提供了高效的序列化和反序列化技术,并支持复杂对象处理。
像 Gson 这类库能直接将 JSON 映射到POJO。但实际开发中,我们常需要排除某些字段的序列化或反序列化操作。
本文将深入探讨 Gson 中两个核心注解:@Expose
和 @SerializedName
。虽然两者都与字段的可序列化性相关,但适用场景截然不同。
2. Gson 环境搭建
在项目中使用 Gson,只需在 pom.xml
添加 Maven 依赖:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
3. @Expose 注解详解
Gson 默认会序列化/反序列化 POJO 类的所有字段,除非明确指定。**@Expose
注解能覆盖此默认行为,精确控制字段的序列化/反序列化**。
当字段的 serialize
和 deserialize
属性为 true
时,Gson 才会处理该字段。这两个属性默认值均为 true
。
看个实际例子。我们定义一个 User
类,包含 id
、name
、age
和 email
字段。由于邮箱是敏感信息,需排除在序列化之外:
public class User {
@Expose
String name;
@Expose
int age;
@Expose(serialize = true, deserialize = false)
long id;
@Expose(serialize = false, deserialize = false)
private String email;
// 构造方法、Getter 和 Setter
}
上述代码中:
name
和age
字段仅标注@Expose
,未显式指定属性值,默认启用序列化和反序列化email
字段显式禁用序列化和反序列化,但需配合GsonBuilder.excludeFieldsWithoutExposeAnnotation()
才能生效id
字段允许序列化但禁止反序列化
验证代码:
@Test
public void givenUserObject_whenSerialized_thenCorrectJsonProduced() {
User user = new User("John Doe", 30, "john.doe@example.com");
user.setId(12345L);
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
String json = gson.toJson(user);
// 验证 name, age, id 被序列化,email 被排除
assertEquals("{\"name\":\"John Doe\",\"age\":30,\"id\":12345}", json);
}
@Test
public void givenJsonInput_whenDeserialized_thenCorrectUserObjectProduced() {
String jsonInput = "{\"name\":\"Jane Doe\",\"age\":25,\"id\":67890,\"email\":\"jane.doe@example.com\"}";
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation()
.create();
User user = gson.fromJson(jsonInput, User.class);
// 验证 name 和 age 被反序列化,email 和 id 被忽略
assertEquals("Jane Doe", user.name);
assertEquals(25, user.getAge());
assertEquals(0, user.getId()); // id 未被反序列化
assertNull(user.getEmail()); // email 未被反序列化
}
✅ 关键点:
- 序列化测试中 JSON 不包含
email
字段 - 反序列化测试中
User
对象忽略 JSON 里的email
和id
字段
4. @SerializedName 注解详解
@SerializedName
注解用于解决字段名映射问题。当 Java 类属性名与 JSON 字段名不一致时,此注解能建立映射关系。
继续使用 User
类示例,假设 JSON 中需要用 firstName
替代 name
:
public class User {
@Expose
@SerializedName("firstName")
String name;
}
对应的单元测试:
@Test
public void givenUserObject_whenSerialized_thenCorrectJsonProduced() {
User user = new User("John Doe", 30, "john.doe@example.com");
user.setId(12345L);
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
String json = gson.toJson(user);
assertEquals("{\"firstName\":\"John Doe\",\"age\":30,\"id\":12345}", json);
}
@SerializedName
还支持 alternate
属性,用于处理多版本兼容问题。当外部系统使用不同字段名时(如 fullName
或 name
),可通过备选名称列表实现兼容:
public class User {
@Expose
@SerializedName(value = "firstName", alternate = { "fullName", "name" })
String name;
}
测试多名称反序列化:
@Test
public void givenJsonWithAlternateNames_whenDeserialized_thenCorrectNameFieldMapped() {
String jsonInput1 = "{\"firstName\":\"Jane Doe\",\"age\":25,\"id\":67890,\"email\":\"jane.doe@example.com\"}";
String jsonInput2 = "{\"fullName\":\"John Doe\",\"age\":30,\"id\":12345,\"email\":\"john.doe@example.com\"}";
String jsonInput3 = "{\"name\":\"Alice\",\"age\":28,\"id\":54321,\"email\":\"alice@example.com\"}";
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
User user1 = gson.fromJson(jsonInput1, User.class);
User user2 = gson.fromJson(jsonInput2, User.class);
User user3 = gson.fromJson(jsonInput3, User.class);
// 验证不同 JSON 字段名正确映射到 name 属性
assertEquals("Jane Doe", user1.getName());
assertEquals("John Doe", user2.getName());
assertEquals("Alice", user3.getName());
}
⚠️ 踩坑提醒:alternate
属性只影响反序列化,序列化时始终使用 value
指定的主名称。
5. 两大注解核心差异
对比维度 | @SerializedName | @Expose |
---|---|---|
核心功能 | 映射 Java 字段与 JSON 字段名 | 控制字段的序列化/反序列化行为 |
必需属性 | value (必填) |
无必填属性 |
可选属性 | alternate (备选名称列表) |
serialize /deserialize (布尔值) |
生效条件 | 开箱即用 | 必须配合 GsonBuilder.excludeFieldsWithoutExposeAnnotation() |
典型场景 | 处理字段名不一致问题 | 敏感字段过滤、版本兼容控制 |
✅ 简单粗暴总结:
- 需要改字段名?用
@SerializedName
- 需要控制字段是否参与序列化?用
@Expose
- 两者可组合使用(如示例中的
name
字段)
6. 总结
本文详细解析了 Gson 中 @SerializedName
和 @Expose
的工作原理及使用场景。核心要点:
@Expose
通过serialize
/deserialize
属性精确控制字段生命周期@SerializedName
解决字段名映射问题,alternate
属性增强兼容性- 两者组合使用能灵活处理复杂 JSON 转换需求
完整代码示例可在 GitHub 获取。