将Web API的JSON解析为List<Rank>时出现ClassCastException错误求助
解析JSON到List时出现ClassCastException与NullPointerException问题
问题概述
尝试将Web API返回的JSON解析为Rank类的ArrayList时,触发ClassCastException: Cannot cast com.google.gson.JsonPrimitive to com.google.gson.JsonObject,后续还引发了NullPointerException。
错误日志
[19:11:59 WARN]: java.lang.ClassCastException: Cannot cast com.google.gson.JsonPrimitive to com.google.gson.JsonObject [19:11:59 WARN]: at java.lang.Class.cast(Unknown Source) [19:11:59 WARN]: at com.google.gson.Gson.fromJson(Gson.java:690) [19:11:59 WARN]: at me.ma1de.juke.shared.api.APIRequest.getJsonResponse(APIRequest.java:54) [19:11:59 WARN]: at me.ma1de.juke.shared.api.APIHandler.request(APIHandler.java:27) [19:11:59 WARN]: at me.ma1de.juke.shared.api.APIHandler.request(APIHandler.java:52) [19:11:59 WARN]: at me.ma1de.juke.shared.rank.RankHandler.refresh(RankHandler.java:44) [19:11:59 WARN]: at org.bukkit.craftbukkit.v1_8_R3.scheduler.CraftTask.run(CraftTask.java:71) [19:11:59 WARN]: at org.bukkit.craftbukkit.v1_8_R3.scheduler.CraftAsyncTask.run(CraftAsyncTask.java:53) [19:11:59 WARN]: at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) [19:11:59 WARN]: at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) [19:11:59 WARN]: at java.lang.Thread.run(Unknown Source) [19:11:59 WARN]: Exception in thread "Craft Scheduler Thread - 48" [19:11:59 WARN]: org.apache.commons.lang.UnhandledException: Plugin juke-bukkit v1.0-SNAPSHOT generated an exception while executing task 2 at org.bukkit.craftbukkit.v1_8_R3.scheduler.CraftAsyncTask.run(CraftAsyncTask.java:56) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source) Caused by: java.lang.NullPointerException at me.ma1de.juke.shared.rank.RankHandler.refresh(RankHandler.java:44) at org.bukkit.craftbukkit.v1_8_R3.scheduler.CraftTask.run(CraftTask.java:71) at org.bukkit.craftbukkit.v1_8_R3.scheduler.CraftAsyncTask.run(CraftAsyncTask.java:53) ... 3 more
相关代码
RankHandler.refresh()方法
this.ranks = new Gson().fromJson(Juke.INSTANCE.getApiHandler().request("/ranks").getJsonResponse(), new TypeToken<List<Rank>>() {}.getType());
Rank类定义
@Getter @AllArgsConstructor public class Rank { private String id, displayName, prefix, inheritFromId; private ChatColor color; private List<String> permissions; private int weight; private boolean staff; }
待解析JSON内容
[{"id":"default","displayName":"Default","prefix":"","color":"WHITE","permissions":[],"weight":0,"staff":false}]
项目依赖配置
<dependencies> <dependency> <groupId>org.spigotmc</groupId> <artifactId>spigot-api</artifactId> <version>1.8.8-R0.1-SNAPSHOT</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.26</version> </dependency> <dependency> <groupId>me.ma1de.juke.shared</groupId> <artifactId>Shared</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.6</version> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>31.1-jre</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.12.0</version> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>4.10.0</version> </dependency> </dependencies>
已尝试方案
将Gson版本从2.10.1降级到2.8.6,问题未解决。
解决方案
1. 排查APIRequest.getJsonResponse()实现问题
从异常栈可以看出,ClassCastException发生在APIRequest.getJsonResponse()方法内部,说明该方法试图将JsonPrimitive强制转换为JsonObject。核心原因是:
- API返回的是JSON数组(
[...]),但getJsonResponse()内部错误地尝试将其解析为JsonObject,导致Gson将响应解析为JsonPrimitive(比如响应是带引号的字符串形式JSON),或者直接解析时类型不匹配。
修复建议:
- 修改
APIRequest.getJsonResponse(),使其返回原始JSON字符串,而非尝试解析为JsonObject;或者返回JsonElement而非强制转换为JsonObject。
2. 调整解析逻辑
如果无法修改APIRequest类,可在调用时先获取原始响应字符串再解析:
// 修改后的refresh方法 String rawResponse = Juke.INSTANCE.getApiHandler().request("/ranks").getRawResponse(); // 假设新增获取原始字符串的方法 if (rawResponse == null || rawResponse.trim().isEmpty()) { this.ranks = new ArrayList<>(); return; } this.ranks = new Gson().fromJson(rawResponse, new TypeToken<List<Rank>>() {}.getType());
若getJsonResponse()返回JsonElement,则做类型判断后解析:
JsonElement jsonElement = Juke.INSTANCE.getApiHandler().request("/ranks").getJsonResponse(); if (jsonElement != null && jsonElement.isJsonArray()) { this.ranks = new Gson().fromJson(jsonElement, new TypeToken<List<Rank>>() {}.getType()); } else { this.ranks = new ArrayList<>(); }
3. 补充Rank类的无参构造函数
Gson默认需要类的无参构造函数来实例化对象,当前Rank类仅用@AllArgsConstructor,会覆盖默认无参构造,可能导致解析失败。添加@NoArgsConstructor:
@Getter @AllArgsConstructor @NoArgsConstructor // 添加无参构造 public class Rank { private String id, displayName, prefix, inheritFromId; private ChatColor color; private List<String> permissions; private int weight; private boolean staff; }
4. 空值防护
在解析前对响应结果判空,避免触发NullPointerException,如上述代码中对rawResponse或jsonElement的非空判断。
内容的提问来源于stack exchange,提问作者ma1de
相关产品推荐
相关产品推荐

