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 注解能覆盖此默认行为,精确控制字段的序列化/反序列化**。

当字段的 serializedeserialize 属性为 true 时,Gson 才会处理该字段。这两个属性默认值均为 true

看个实际例子。我们定义一个 User 类,包含 idnameageemail 字段。由于邮箱是敏感信息,需排除在序列化之外:

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
}

上述代码中:

  • nameage 字段仅标注 @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 里的 emailid 字段

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 属性,用于处理多版本兼容问题。当外部系统使用不同字段名时(如 fullNamename),可通过备选名称列表实现兼容:

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 的工作原理及使用场景。核心要点:

  1. @Expose 通过 serialize/deserialize 属性精确控制字段生命周期
  2. @SerializedName 解决字段名映射问题,alternate 属性增强兼容性
  3. 两者组合使用能灵活处理复杂 JSON 转换需求

完整代码示例可在 GitHub 获取。