JSONObject.optLong()丢失精度的问题

这里写自定义目录标题

JSONObject为Android系统中的org.json.JSONObject。

问题发现

review代码时,看到一个提交记录,修复了JSONObject optLong丢失精度的问题。
更改前为

long id = jsonObject.optLong(idKey)

更改后为

    long id = optLong(jsonObject, idKey)
    private long optLong(JSONObject jsonObject, String key) {
        try {
            return Long.valueOf(jsonObject.optString(key));
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }

        return jsonObject.optLong(key);
    }

解析的是服务端下发的json数据,为何JSONObject.optLong()会丢失精度呢?

Demo验证

写个demo测了下

		val jsonObj = JSONObject()
        jsonObj.put("long_str", (Long.MAX_VALUE -1).toString()) // 注意这里!!!
        Log.e("sss", "$jsonObj") 
        val doubleValue = jsonObj.optDouble("long_str")
        val longValue = jsonObj.optLong("long_str")
        val longFromString = getLongFromStr(jsonObj)
        Log.e("sss", "$longValue $longFromString ${longValue == longFromString} $doubleValue")

其中,getLongFromStr 为

private fun getLongFromStr(jsonObj: JSONObject): Long {
        try {
            return jsonObj.optString("long_str").toLong()
        } catch (e: NumberFormatException) {
            e.printStackTrace()
        }
        return jsonObj.optLong("long_str")

    }

看下输出结果:

E/sss: {"long_str":"9223372036854775806"}
E/sss: 9223372036854775807 9223372036854775806 false 9.223372036854776E18

!!!! 通过optString方式取的值是对的,通过optLong方式取的值却是错的!!!
但如果简单改下demo

        val jsonObj = JSONObject()
        jsonObj.put("long_str", Long.MAX_VALUE -1) // 改动点
        Log.e("sss", "$jsonObj")
        val doubleValue = jsonObj.optDouble("long_str")
        val longValue = jsonObj.optLong("long_str")
        val longFromString = getLongFromStr(jsonObj)
        Log.e("sss", "$longValue $longFromString ${longValue == longFromString} $doubleValue")

输出结果却是:

E/sss: {"long_str":9223372036854775806}
E/sss: 9223372036854775806 9223372036854775806 true  9.223372036854776E18

!!!optString()和optLong()两种方法取的值都是对的!!!

分析

看下JSONObject的optLong()方法

    public long optLong(@Nullable String name, long fallback) {
        Object object = opt(name);
        Long result = JSON.toLong(object);
        return result != null ? result : fallback;
    }

内部是通过JSON.toLong()方法实现的

    static Long toLong(Object value) {
        if (value instanceof Long) {
            return (Long) value;
        } else if (value instanceof Number) {
            return ((Number) value).longValue();
        } else if (value instanceof String) {
            try {
                return (long) Double.parseDouble((String) value);
            } catch (NumberFormatException ignored) {
            }
        }
        return null;
    }

如果Object为Long的话,直接走第一个if分支,不存在精度损失;
如果Object为String的话,走第三个if分支,先将String类型的数解析成双精度浮点型,再转成Long型,这就可能造成精度丢失,具体原因可参考float,double等精度丢失问题

经验

在做JSON构造和解析时,应确定好数据格式,避免出现数据丢失的情况。对于客户端而言,JSON解析时,数据类型可以统一先转成String,再做String到相应数据类型的转换处理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值