当前位置: 首页 > ai >正文

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

前文

在 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;  // 小写xprivate double y;  // 小写yprivate 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 和解析后的对象,对比排查字段映射问题

http://www.xdnf.cn/news/17631.html

相关文章:

  • Qt——信号和槽
  • 移动端网页调试实战,键盘弹出与视口错位问题的定位与优化
  • iOS 签名证书实践日记,我的一次从申请到上架的亲历
  • vue项目封装axios请求,支持判断当前环境及判断token是否过期等等(详细教程,可复制粘贴代码)
  • UE官方文档学习 C++ TAarry 查询(三)Contain,Find函数的使用
  • Java面试题储备11: mysql优化全面讲一下,及你遇到的对应业务场景
  • 第六十三章:AI模型的“跨界之旅”:不同硬件架构下的兼容性方案
  • RK3568 Linux驱动学习——Linux LED驱动开发
  • 数据分析与可视化
  • Java的异常机制
  • Supabase快速入门与实战指南
  • Effective C++ 条款37:绝不重新定义继承而来的缺省参数值
  • 存储过程作为系统逻辑核心的架构思考 —— 以 SaaS 系统为例
  • 计算机视觉(8)-纯视觉方案实现端到端轨迹规划(模型训练+代码)
  • 数据库规范化:消除冗余与异常的核心法则
  • 经济基础知识第一节:物质资料生产和基本经济规律(一)
  • SQL 与 NoSQL 的核心区别
  • 为什么灰度图用G(绿色)通道?
  • Docker 101:面向初学者的综合教程
  • 【报错处理】mount: /boot/efi: unknown filesystem type ‘LVM2_member‘.
  • 记录一次react渲染优化
  • 实现文字在块元素中水平/垂直居中详解
  • 教程 | 用Parasoft SOAtest实现高效CI回归测试
  • AWS EKS 常用命令大全:从基础管理到高级运维
  • [激光原理与应用-257]:理论 - 几何光学 - 光束整形
  • Springboot注册过滤器的三种方式(Order 排序)
  • Spring Cloud系列—Config配置中心
  • 【Oracle APEX开发小技巧16】交互式网格操作内容根据是否启用进行隐藏/展示
  • VS4210芯片技术资料(IT6604+VS4210+MDIN380连接原理图)
  • 基于STC8单片机的RTC时钟实现:从原理到实践