商城首页欢迎来到中国正版软件门户

您的位置:首页 >Gson解析JSON数组转List方法详解

Gson解析JSON数组转List方法详解

  发布于2026-04-16 阅读(0)

扫一扫,手机访问

如何正确使用 Gson 将 JSON 数组反序列化为 List 对象

本文详解 Gson 反序列化时出现 ClassCastException: Cannot cast JsonPrimitive to JsonObject 的根本原因,重点解决 ChatColor 类型不匹配、缺失字段及 JSON 结构误判等问题,并提供可直接复用的修复方案。

本文详解 Gson 反序列化时出现 `ClassCastException: Cannot cast JsonPrimitive to JsonObject` 的根本原因,重点解决 `ChatColor` 类型不匹配、缺失字段及 JSON 结构误判等问题,并提供可直接复用的修复方案。

该错误看似发生在 Gson 解析阶段(fromJson(...)),实则根源在于 Java 类型定义与 JSON 数据结构严重不一致。从你提供的 JSON 示例 ["{...}"] 可知,API 返回的是标准 JSON 数组,但 Gson 在尝试将其中某个元素(如字符串 "WHITE")强制转为 JsonObject 时失败——这通常意味着 Gson 正在解析一个本应是对象(JsonObject)的位置,却遇到了原始值(JsonPrimitive),典型诱因有以下三点:

? 根本原因分析

  1. ChatColor color 字段类型失配
    JSON 中 "color": "WHITE" 是字符串字面量,而你的 Rank 类中声明为 ChatColor(Spigot 的枚举类)。Gson 默认无法自动将字符串 "WHITE" 映射为 ChatColor.WHITE,若未注册自定义 TypeAdapter,Gson 会尝试以泛型方式解析该字段,最终因类型擦除和内部反射逻辑错乱,触发 JsonPrimitive → JsonObject 的非法强转。

  2. inheritFromId 字段缺失且无默认值
    JSON 示例中未包含 "inheritFromId" 字段,而 Rank 构造器为全参 @AllArgsConstructor,Lombok 会生成要求所有字段非 null 的构造函数。Gson 在反序列化时若找不到对应键,将传入 null,导致后续构造失败或空指针异常(日志末尾的 NullPointerException 已佐证)。

  3. JSON 响应体可能非纯数组(隐藏陷阱)
    虽然你确认返回的是 [{"id":"default",...}],但需严格验证 getJsonResponse() 方法是否始终返回合法 JSON 数组字符串。常见错误包括:

    • API 错误时返回 {"error":"..."}(单个对象而非数组)
    • 网络异常返回空字符串或 HTML 错误页
    • getJsonResponse() 内部逻辑意外截断/拼接了非 JSON 内容

✅ 正确解决方案

步骤 1:为 ChatColor 注册 TypeAdapter(关键!)

// 创建 Gson 实例时注入适配器
Gson gson = new GsonBuilder()
    .registerTypeAdapter(ChatColor.class, new JsonDeserializer<ChatColor>() {
        @Override
        public ChatColor deserialize(JsonElement json, Type typeOfT,
                                   JsonDeserializationContext context) throws JsonParseException {
            if (json.isJsonNull()) return null;
            String colorName = json.getAsString().toUpperCase();
            try {
                return ChatColor.valueOf(colorName);
            } catch (IllegalArgumentException e) {
                // 处理未知颜色名,例如回退到 WHITE 或抛出定制异常
                return ChatColor.WHITE;
            }
        }
    })
    .create();

步骤 2:修正 Rank 类结构(兼容缺失字段)

@Getter
@NoArgsConstructor // 必须添加无参构造器,Gson 反序列化必需
@AllArgsConstructor(access = AccessLevel.PRIVATE) // 私有全参构造,避免外部滥用
public class Rank {
    private String id;
    private String displayName;
    private String prefix;
    private String inheritFromId; // 改为 String 类型(更安全),或设为 @Nullable
    private ChatColor color;
    private List<String> permissions;
    private int weight;
    private boolean staff;

    // 若需保留继承关系,可添加带默认值的构造器
    public Rank(String id, String displayName, String prefix, String inheritFromId,
                ChatColor color, List<String> permissions, int weight, boolean staff) {
        this.id = id;
        this.displayName = displayName;
        this.prefix = prefix;
        this.inheritFromId = inheritFromId != null ? inheritFromId : ""; // 安全默认值
        this.color = color;
        this.permissions = permissions != null ? permissions : new ArrayList<>();
        this.weight = weight;
        this.staff = staff;
    }
}

步骤 3:健壮的反序列化调用(含错误防护)

public void refresh() {
    String jsonResponse = Juke.INSTANCE.getApiHandler().request("/ranks").getJsonResponse();

    // 强制校验 JSON 合法性(防御性编程)
    if (jsonResponse == null || jsonResponse.trim().isEmpty()) {
        throw new IllegalStateException("API returned empty or null JSON response");
    }

    try {
        // 使用预配置的 Gson 实例
        Type listType = new TypeToken<List<Rank>>() {}.getType();
        this.ranks = gson.fromJson(jsonResponse, listType);

        // 可选:验证解析结果
        if (this.ranks == null) {
            throw new IllegalStateException("Gson failed to parse JSON into List<Rank>");
        }

    } catch (JsonSyntaxException e) {
        throw new RuntimeException("Invalid JSON syntax in /ranks response: " + jsonResponse, e);
    } catch (JsonParseException e) {
        throw new RuntimeException("Failed to parse Rank objects: " + e.getMessage(), e);
    }
}

⚠️ 注意事项

  • 切勿依赖 @AllArgsConstructor + @Getter 的组合进行反序列化:Gson 需要无参构造器或字段级访问权限,Lombok 的全参构造器会阻止 Gson 的默认字段注入机制。
  • 始终验证 API 响应内容:在 getJsonResponse() 中添加日志输出原始字符串,确认其真实结构(尤其注意引号、括号、编码问题)。
  • 版本兼容性提示:Gson 2.8.6 与 2.10.1 均存在此问题,本质与版本无关,而是类型契约不一致所致;升级 Gson 并不能绕过类型映射缺陷。

通过以上三步改造,即可彻底解决 ClassCastException,并构建出稳定、可维护的 JSON 反序列化流程。

本文转载于:互联网 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注