【Android】【bug】Json解析错误Expected BEGIN_OBJECT but was STRING...

前文

在 Android 开发中,JSON 解析是对接后端接口、处理设备数据时的常见操作,而 Gson 作为谷歌推出的 JSON 解析库,以其简洁高效被广泛使用。但在实际开发中,解析过程往往会遇到各种 "坑",本文就以一次雷达数据解析为例,分享从报错到数据异常的完整解决过程。

一、初始问题:

解析时报 "Expected BEGIN_OBJECT but was STRING"

问题场景

项目中需要解析雷达设备通过 TCP 发送的 JSON 数据,数据格式如下:

json 复制代码
{"topic":"/Radar/data","data":"{\"sn\":20250001,\"time\":\"2025-08-12-16:56:22\",\"X\":-2.2384777069091799,\"Y\":1.7031339406967164,\"Z\":0.0}"}

解析代码片段(初始版本):

java 复制代码
// 直接尝试解析为内层数据类
RadarInfo radarInfo = gson.fromJson(dataString, RadarInfo.class);

运行后报错:

java 复制代码
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 32 path $.data
问题分析

从报错信息和数据格式可以看出,核心问题是JSON 结构嵌套方式不匹配:

外层 JSON 的data字段值被双引号包裹,实际是一个 "字符串形式的 JSON"(而非直接的 JSON 对象)

解析代码错误地将其当作 "嵌套的 JSON 对象" 处理,导致 Gson 预期接收一个对象(BEGIN_OBJECT),却收到了字符串(STRING)

解决方案:两步解析法

需要分两次解析,先处理外层 JSON,再解析内层字符串:

定义外层数据模型:

接收topic和字符串类型的data

java 复制代码
private static class RadarData {
    private long sn;
    private String data; // 注意:这里是String类型,接收内层JSON字符串
}
先解析外层 JSON,获取data字段的字符串值
java 复制代码
// 第一步:解析外层,得到包含data字符串的对象
RadarData radarData = gson.fromJson(dataString, RadarData.class);
再解析内层字符串,转换为实际数据对象
java 复制代码
// 第二步:将data字符串解析为具体数据
RadarInfo radarInfo = gson.fromJson(radarData.data, RadarInfo.class);
二、新问题:解析成功但数据全为 0.0
问题场景

解决上述报错后,解析过程不再崩溃,但日志显示所有坐标值都为 0:

java 复制代码
雷达数据位置X: 0.0
雷达数据位置Y: 0.0
雷达数据位置Z: 0.0
内层数据类定义(问题版本):
java 复制代码
private static class RadarInfo {
    private String time;
    private double x;  // 小写x
    private double y;  // 小写y
    private double z;  // 小写z
}
问题分析

通过对比 JSON 数据和 Java 类发现:字段名大小写不匹配

原始 JSON 中坐标字段是大写的X、Y、Z(如"X":-2.2384777069091799)

数据类中定义的是小写的x、y、z

Gson 默认采用 "严格字段名匹配"(包括大小写),因此无法将 JSON 中的X映射到x,最终赋值为 double 类型的默认值 0.0

解决方案:统一字段映射关系

有两种方式可以解决字段名大小写问题:

方案一:修改属性名与 JSON 保持一致

直接将数据类的属性名改为大写,与 JSON 字段匹配:

java 复制代码
private static class RadarInfo {
    private String time;
    private double X;  // 大写X,与JSON一致
    private double Y;  // 大写Y,与JSON一致
    private double Z;  // 大写Z,与JSON一致
}
方案二:使用@SerializedName注解(推荐)

通过注解指定 JSON 字段名,无需修改 Java 属性名,灵活性更高:

java 复制代码
import com.google.gson.annotations.SerializedName;

private static class RadarInfo {
    private String time;
    
    @SerializedName("X")  // 明确指定对应JSON中的"X"字段
    private double x;
    
    @SerializedName("Y")  // 明确指定对应JSON中的"Y"字段
    private double y;
    
    @SerializedName("Z")  // 明确指定对应JSON中的"Z"字段
    private double z;
}
三、总结:Gson 解析避坑指南

通过本次问题解决,我们可以总结出 Gson 解析 JSON 时的常见问题及排查思路:

结构不匹配报错(Expected BEGIN_OBJECT but was STRING)

检查是否为 "JSON 嵌套字符串形式的 JSON",需分两次解析

确认外层字段类型(如data是 String 还是 Object)

数据为默认值(如 0、null)

检查字段名是否完全匹配(包括大小写、拼写)

核对数据类型是否一致(如 JSON 的 number 对应 Java 的 int/double,而非 String)

最佳实践

定义数据类时尽量使用@SerializedName注解,明确映射关系

解析嵌套 JSON 时,分步骤处理(先外层后内层)

遇到问题时,打印原始 JSON 和解析后的对象,对比排查字段映射问题

相关推荐
麻瓜生活睁不开眼5 小时前
Android16修改全局桌面视图边框四直角显示为弧边圆角
android·java·深度学习
AFinalStone8 小时前
Android 7系统休眠唤醒(六)唤醒全链路
android·电源管理·休眠唤醒
雨白9 小时前
深入理解 Kotlin 协程 (九):互通有无,解构 Channel 缓冲策略与底层无锁机制
android·kotlin
灯塔@kuaidao11 小时前
平台交叉编译名词解释与基础流程
android
__Witheart__11 小时前
3568 Android otg模式下adb热拔插不识别
android·adb·rockchip
SQDN12 小时前
Chatbox 1.22.1 获取不到模型?先验 /models,再对齐精确 model ID
人工智能·测试工具·机器学习·chatgpt·json
明天…ling13 小时前
Upload-Labs (Pass1-Pass21) 完整通关思路与源码分析
android·网络安全·渗透测试·burpsuite·upload-labs·文件上传绕过·靶场复现
__Witheart__13 小时前
3568 Android ntp校时使用
android·rockchip
wddptwd2814 小时前
android studio 报错怎么处理 java.lang.NullPointerException
android·java·android studio
美狐美颜SDK开放平台15 小时前
直播APP开发,美颜SDK和相机SDK有什么区别?
android·深度学习·数码相机·ios·直播美颜sdk·视频美颜sdk