资讯专栏INFORMATION COLUMN

使用 JSONObject 需要注意避免的一个问题

rozbo / 2094人阅读

摘要:作者介绍鲍协浩,小米部门,基础应用组通讯录开发负责人问题现象在业务同步的逻辑代码中,使用到了来解析服务端的数据。

作者介绍:鲍协浩,小米MIUI部门, MIUI基础应用组通讯录开发负责人

问题现象

在 Android 业务同步的逻辑代码中,使用到了 JSONObject 来解析服务端的 JSON 数据。同时本地因为业务新增需求的缘故,在本地数据库中使用 JSONObject 缓存了包括水位等同步相关的信息,其中,水位值是 Long 型。但近期发现同步过程中下一次同步时,传递给服务器的水位并不是上一次服务器返回的新水位,而是相差一些。以 301028292893495297L 为例,服务器返回这个水位之后,下次客户端上传的水位是 301028292893495296L,差值为 -1。

问题排查

通过反复排查代码逻辑,发现水位从服务端返回到下次请求之间,只经过了以下转换:

认真阅读代码不难发现,Long 型的水位值保存在 JSON 对象中的时候转成了 String 型,而在读取的时候又当作是 Long 型来处理。因此会有精度缺失的问题,参见如下 JSONObject 的文档:

由此可见,在读取 JSON 对象的某个值时,如果原先是 String 型,读取的时候当作是 Long 型,是会将 String 型通过 Double 进行解析的,所以在值超过 2^52 时会有精度缺失的问题。于是,遇到的问题就可以解释了。以下是 Double 的存储格式规范:

其中,Double 和 Long 的精度测试代码很简单(输入参数可以提供例如 301028292893495297L 这样超过 2^52 的 long 值,会发现其返回值不为 0):

Double 和 Long 的精度测试代码很简单(输入参数可以提供例如 301028292893495297L 这样超过 2^52 的 long 值):

知道了问题的根源,修复就一目了然了,在水位保存在 JSONObject 对象中时,应该当作 Long 型而不是 String 型来保存;亦或者在读取的时候也当作是 String 型,然后通过 Long.valueOf 等接口进行解析。

另外,关于 JSON 对象中的值是 Long 型还是 String 型,其实比较容易被忽略。如果JSON 对象在使用 String 表示的时候,该值对应处有引号就是 String 型。看如下的试用例就一目了然了:

类似的问题在网上随意一搜,其实有许多人遇坑了,比如这个。

所以,尽管不能说这个库的设计是很失败的,但肯定不算是一个设计良好的库。因为你无法直接从 API 名称看出其内在的潜在逻辑,容易导致使用者使用不当。因此,经验教训就是:使用第三方库的时候,能看 API 文档就看 API 文档,切不可望文生义。当然,这个问题可能也仅限在 Android 中较老的代码模块,毕竟新的代码都会使用 GSON 等类库进行 JSON 对象操作,也就不容易出现这样的不易发现的问题了。

当然,单就这个问题来看,其实是在新增业务逻辑的时候,没有正确使用 JSONObject 对象的接口,Long 型的值不应当看成是 String 型进行保存而又当成是 Long 型来读取,如果保存和读取的接口保持对应,也就不会出现问题了。不管怎么说,该问题的教训是在使用 JSONObject 相关接口时要倍加小心谨慎。

备注:Github 上最新的 JSON-Java 库没有这个问题,可以放心使用。

问题解决

知道了问题的根源,修复就一目了然了,在水位保存在 JSON 对象中时,应该当作 Long 型而不是 String 型来保存;或者在读取的时候也当作是 String 型,然后通过 Long.valueOf 等接口进行解析。

问题后话

类似的问题在网上随意一搜,其实有许多人遇坑了,比如这个。所以,尽管不能说这个库的设计是很失败的,但肯定不算是一个设计良好的库。因为你无法直接从 API 名称看出内在的潜在逻辑,导致使用不当。因此,经验教训就是:使用第三方库的时候,能看 API 文档就看 API 文档,切不可望文生义。

当然,Github 上最新的 JSON-Java 库是没有这个问题的。

小米开放平台重磅推出小米帐号接入有礼活动:成功接入小米帐号即可获得小米开放平台免费提供的平台资源(小米应用商店、小米卡包、小米推送vip、小米帐号联盟等资源),机会不容错过,我们期待您的加入!
活动报名地址:http://dev.xiaomi.com/console...

官方QQ交流群:398616987
想要了解更多?
那就关注我们吧!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/13417.html

相关文章

  • 使用 JSONObject 需要注意避免一个问题

    摘要:作者介绍鲍协浩,小米部门,基础应用组通讯录开发负责人问题现象在业务同步的逻辑代码中,使用到了来解析服务端的数据。 作者介绍:鲍协浩,小米MIUI部门, MIUI基础应用组通讯录开发负责人 问题现象 在 Android 业务同步的逻辑代码中,使用到了 JSONObject 来解析服务端的 JSON 数据。同时本地因为业务新增需求的缘故,在本地数据库中使用 JSONObject 缓存了包括...

    sutaking 评论0 收藏0
  • poi几多愁,恰似源码温柔

    摘要:拿到值后,创建当前单元格,把数据填充进去。首先判断当前单元格的数据是不是数字型的,如果是数字型的,在判断是不是日期类型的,如果是日期类型,再转为日期类型。 导读 最近,公司在做导入导出的项目,首先想到的是poi的导入和导出。如果每次导入和导出都要重写的话,那么,实在是浪费时间和精力。于是,封装了原生的poi的导入和导出。在封装的时候,就会出现一系列的问题。 在进行导入和导出的时候,我们...

    zhiwei 评论0 收藏0
  • Java 编程:如何提高性能?(简单总结篇)

    摘要:开发者在编程中除了要有编程规范,还要注意性能哦,今天就工作中遇到的一些问题进行了一个简单总结,希望攻城狮来批评指正。 开发者在编程中除了要有编程规范,还要注意性能,在 Java 编程中有什么提高性能的好办法呢? 本文转自国内 ITOM 行业领军企业 OneAPM Cloud Insight(一款能够优雅监控多种操作系统、数据库、中间件、云主机的解决方案)美女工程师陈永梅简书。 开发者在...

    fuchenxuan 评论0 收藏0
  • Gson基本操作,JsonObject,JsonArray,String,JavaBean,List

    摘要:代码本身不难,容易看懂,但前面说到,这里我故意这样写,因为需要说两个东西从名称我们就可以看出,这是一个解析类。没错,它可以把数据分别通过和解析成和。这跟普通的解析差不多,不展开说。只贴代码不作解释,如想详细了解,看文末链接。  (转自)https://www.cnblogs.com/robbinluobo/p/7217387.html String、JsonObject、JavaBe...

    AnthonyHan 评论0 收藏0
  • android + php 后台开发

    摘要:安卓与服务器的数据交互在我们进行开发的时候,避免不了的要进行登录注册,个人信息获取,数据交互等等这一系列的操作。而与之间的数值传递是通过数据类型。创建对象并添加数据。第五步就是进行数据的解析,获取后台返回的数据。android+php 安卓与服务器的数据交互 在我们进行android开发的时候,避免不了的要进行登录注册,个人信息获取,数据交互等等这一系列的操作。这样就需要进行android端...

    warkiz 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<