复盘一次Gson的坑,数字类型被自动转化为Double

4,702 阅读1分钟

需求

这个需求很简单,需要将一个json object,转为一个Map<String, Any>类型,当然这个json object保证key是string类型。

func convert(json: JsonObject): Map<String, Any> 

实现

目前有两种实现方式。 方法一:

fun convertLoop(jsonObject: JsonObject): Map<String, Any> {
    val data = mutableMapOf<String, Any>()
    for (key in jsonObject.keySet()) {
        data[key] = jsonObject[key]
    }
    return data
}

方法二:

fun convertGson(jsonObject: JsonObject): Map<String, Any> {
    val mapTypeToken = object : TypeToken<Map<String, Any>>() {}.type
    return GsonBuilder().create().fromJson<Map<String, Any>>(jsonObject, mapTypeToken)
}

看上去方法二相对于方法一更加简洁,不需要遍历,只需要执行Gson自带的方法,但结果怎么样呢?

测试

val jsonObject = JsonObject()
jsonObject.addProperty("name", "zhang")
jsonObject.addProperty("isBoy", true)
jsonObject.addProperty("age", 20)
jsonObject.addProperty("weight", 70.1)
jsonObject.addProperty("timeStamp", System.currentTimeMillis())


Log.e("loopResult", convertLoop(jsonObject).toString())
Log.e("gsonResult", convertGson(jsonObject).toString())

E/loopResult: {name="zhang", isBoy=true, age=20, weight=70.1, timeStamp=1652421933173}
E/gsonResult: {name=zhang, isBoy=true, age=20.0, weight=70.1, timeStamp=1.652421933173E12}

可以看到在gson的转化下, 数字类型出了大问题,明明是age从20变成了20.0, timeStamp更是变成了double类型。 这到底是什么原因?

Gson源码中的坑

stackoverflow.com/questions/1… 可以从stackoverflow中找到相关的解释,原因是gson在解析时,默认将所有Json中的number解析成double类型

case NUMBER:
  return in.nextDouble();

这个问题在gson 2.8.6的版本中也有出现。

解决方式

  1. 使用loop的方式获取数据,可能代码上不够优雅。
  2. 更新Gson到最新的2.9.0版本. 在这个版本中,google添加了ToNumberStrategy,其中默认的策略依然是转化为double,但可以设置为ToNumberPolicy.LONG_OR_DOUBLE.
fun convertGson(jsonObject: JsonObject): Map<String, Any> {
    val mapTypeToken = object : TypeToken<Map<String, Any>>() {}.type
val gson = GsonBuilder().setNumberToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE).setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE).create()
    return gson.fromJson<Map<String, Any>>(jsonObject, mapTypeToken)
}

在设定策略之后,现在输出就正常了