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

发布于:2025-08-13 ⋅ 阅读:(17) ⋅ 点赞:(0)
前文

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

一、初始问题:

解析时报 “Expected BEGIN_OBJECT but was STRING”

问题场景

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

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

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

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

运行后报错:

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

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

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

雷达数据位置X: 0.0
雷达数据位置Y: 0.0
雷达数据位置Z: 0.0
内层数据类定义(问题版本):
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 字段匹配:

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 属性名,灵活性更高:

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


网站公告

今日签到

点亮在社区的每一天
去签到