Gson 中的 @Expose 与 @SerializedName 注解
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 仅会处理带有 @Expose 注解的字段。这两个属性的默认值均为 true。
以下示例展示了一个包含 id、name、age 和 email 的 User 类。由于 email 是敏感信息,我们将其从输出的 JSON 中排除:
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 省略
}
在上述代码片段中,我们用 @Expose 标注了 name 和 age 字段。未显式设置 serialize 和 deserialize 属性意味着它们默认为 true。
需注意 email 属性的两个注解值均设为 false,因此序列化的 JSON 会忽略该字段。但需在创建 GsonBuilder 实例时调用 excludeFieldsWithoutExposeAnnotation() 方法才能使此配置生效。
另一方面,对于 id 字段,序列化过程会包含它,而反序列化时会忽略 JSON 中存在的任何 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 字段。
4、@SerializedName 注解
下面我们了解 Gson 中 @SerializedName 注解的用途。当 Java 类的属性名与其 JSON 表示需要不同时,该注解可将 POJO 属性映射到 JSON 中的指定字段名。
继续之前的示例,假设这次我们需要将 User 类的 name 字段在 JSON 中表示为 firstName:
public class User {
@Expose
@SerializedName("firstName")
String name;
}
测试,验证 firstName 字段:
@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 而非 firstName,通过正确配置 alternate 属性即可准确解析:
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);
// 验证 `name` 字段可从不同的 JSON 字段名正确反序列化。
assertEquals("Jane Doe", user1.getName());
assertEquals("John Doe", user2.getName());
assertEquals("Alice", user3.getName());
}
系统能够正确地从输入 JSON 数据中反序列化 name 属性,无论该属性在 JSON 中被命名为 fullName 还是 firstName。
5、@SerializedName 和 @Expose 的对比
以下是两个注解的核心区别总结:
@SerializedName |
@Expose |
|---|---|
| 将 Java POJO 字段映射到 JSON 字段名 | 标记字段是否应参与序列化或反序列化 |
必须提供 value 属性,可选择使用 alternate 属性 |
提供两个可选属性:serialize 和 deserialize |
| 开箱即用,无需任何配置 | 需通过 GsonBuilder 配置且必须调用 GsonBuilder.excludeFieldsWithoutExposeAnnotation() 才能生效 |
6、总结
本文介绍了如何使用 Gson 中的 @SerializedName 和 @Expose 注解来处理 Java 中的 JSON 序列化与反序列化,同时强调了两者的核心差异。
Ref:https://www.baeldung.com/gson-expose-vs-serializedname-annotations