刚做的网站上线后收不到了/泉州全网营销优化
这里写自定义目录标题
- 问题发现
- Demo验证
- 分析
- 经验
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到相应数据类型的转换处理。