GSON 反序列化时部分字段(如 playerName)为 null,主因是类中使用了 final 字段且缺少无参构造器或 Gson 兼容的访问方式;本文详解根本原因、修复步骤及最佳实践。
gson 反序列化时部分字段(如 `playername`)为 `null`,主因是类中使用了 `final` 字段且缺少无参构造器或 gson 兼容的访问方式;本文详解根本原因、修复步骤及最佳实践。
在使用 Google GSON 进行 JSON 反序列化时,若目标 Java 类中字段声明为 final,而未提供 Gson 可识别的初始化机制(如无参构造器 + setter、@SerializedName 配合非 final 字段、或启用 GsonBuilder.setFieldNamingPolicy 等),Gson 默认无法为 final 字段赋值——它仅通过反射直接写入字段(Field.set()),而 final 字段在运行时被 JVM 保护,反射写入会静默失败(尤其在 JDK 12+ 中默认抛出 IllegalAccessException,旧版可能降级为忽略)。
您原始代码中的 SentPlayer 类全部字段均为 final,且未提供任何 setter 或 Gson 可用的构造策略,导致 Gson 在解析 "playerName": "Rushmore75" 时跳过该字段,最终保留 null。
✅ 正确做法:三选一(推荐方案)
方案 1:移除 final,添加标准 getter/setter(最简单、最兼容)
public class SentPlayer {
@SerializedName("uuid")
private String uuid;
@SerializedName("playerName") // 显式映射,避免命名策略不一致
private String playerName;
@SerializedName("universeUuid")
private String universeUuid;
// 无参构造器(Gson 反序列化必需)
public SentPlayer() {}
// 全参构造器(可选,用于手动创建)
public SentPlayer(String uuid, String playerName, String universeUuid) {
this.uuid = uuid;
this.playerName = playerName;
this.universeUuid = universeUuid;
}
// 必须提供 public setter(Gson 依赖反射调用)
public void setUuid(String uuid) { this.uuid = uuid; }
public void setPlayerName(String playerName) { this.playerName = playerName; }
public void setUniverseUuid(String universeUuid) { this.universeUuid = universeUuid; }
// getter(可选,仅用于业务读取)
public String getUuid() { return uuid; }
public String getPlayerName() { return playerName; }
public String getUniverseUuid() { return universeUuid; }
}
对应 PlayerMsg 类也需保持非 final 字段 + 无参构造器:
public class PlayerMsg {
@SerializedName("player")
public SentPlayer player;
@SerializedName("msg")
public String msg;
public PlayerMsg() {} // 必须!
}
方案 2:保留 final,使用 @JsonCreator + @JsonProperty(更安全、不可变设计)
public class SentPlayer {
private final String uuid;
private final String playerName;
private final String universeUuid;
@JsonCreator
public SentPlayer(
@JsonProperty("uuid") String uuid,
@JsonProperty("playerName") String playerName,
@JsonProperty("universeUuid") String universeUuid) {
this.uuid = uuid;
this.playerName = playerName;
this.universeUuid = universeUuid;
}
// getter 保持 public(Gson 不需要 setter)
public String getUuid() { return uuid; }
public String getPlayerName() { return playerName; }
public String getUniverseUuid() { return universeUuid; }
}
⚠️ 注意:此时 PlayerMsg 也需同样处理,或确保其字段为非 final 并含无参构造器(因 Gson 默认不递归使用 @JsonCreator 到嵌套类,除非显式配置)。
方案 3:全局启用 GsonBuilder 的 setLenient() 和字段访问(不推荐,仅作调试)
Gson gson = new GsonBuilder()
.disableHtmlEscaping()
.serializeNulls()
.create();
⚠️ 但此方式无法解决 final 字段问题——serializeNulls() 仅控制序列化时是否输出 null 值,与反序列化赋值无关。
? 验证示例
String json = """
{
"player": {
"uuid": "",
"playerName": "Rushmore75",
"universeUuid": "9dae671e-91ed-440e722c12"
},
"msg": "1234"
}
""";
Gson gson = new Gson();
PlayerMsg result = gson.fromJson(json, PlayerMsg.class);
System.out.println(result.player.getPlayerName()); // 输出: Rushmore75 ✅
? 关键总结
- ❌ final 字段 + 无 @JsonCreator + 无 setter → Gson 静默跳过赋值,结果为 null;
- ✅ 必须提供 Gson 可用的字段注入路径:无参构造器 + public setter,或 @JsonCreator 构造器注入;
- @SerializedName 强烈建议显式标注,避免因命名策略(如 LOWER_CASE_WITH_UNDERSCORES)导致匹配失败;
- 生产环境避免依赖 final 字段直连 JSON,优先采用不可变 builder 模式或方案 2 的构造器注入。
遵循以上任一方案,即可彻底解决 playerName 等字段反序列化为 null 的问题。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











