【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 和解析后的对象,对比排查字段映射问题

相关推荐
初圣魔门首席弟子2 小时前
友元类和友元函数bug
bug
初圣魔门首席弟子2 小时前
switch缺少break出现bug
c++·算法·bug
毕设源码-邱学长2 小时前
【开题答辩全过程】以 Bug交流网站为例,包含答辩的问题和答案
bug
用户2018792831672 小时前
ANR之RenderThread不可中断睡眠state=D
android
煤球王子2 小时前
简单学:Android14中的Bluetooth—PBAP下载
android
debug 小菜鸟2 小时前
aws 实战小bug
云计算·bug·aws
小趴菜82272 小时前
安卓接入Max广告源
android
齊家治國平天下2 小时前
Android 14 系统 ANR (Application Not Responding) 深度分析与解决指南
android·anr
ZHANG13HAO2 小时前
Android 13.0 Framework 实现应用通知使用权默认开启的技术指南
android
【ql君】qlexcel2 小时前
Android 安卓RIL介绍
android·安卓·ril