如何防止Gson将整数表示为浮点数

时间:2013-03-19 18:47:50

标签: java android gson

当我尝试将字符串转换为json时,Gson有一些奇怪的行为。下面的代码将字符串草稿转换为json响应。有没有办法阻止gson将'.0添加到所有整数值?

ArrayList<Hashtable<String, Object>> responses;
Type ResponseList = new TypeToken<ArrayList<Hashtable<String, Object>>>() {}.getType();
responses = new Gson().fromJson(draft, ResponseList);

draft:
[ {"id":4077395,"field_id":242566,"body":""},
  {"id":4077398,"field_id":242569,"body":[[273019,0],[273020,1],[273021,0]]},
  {"id":4077399,"field_id":242570,"body":[[273022,0],[273023,1],[273024,0]]}
]

responses:
[ {id=4077395.0, body=, field_id=242566.0},
  {id=4077398.0, body=[[273019.0, 0.0], [273020.0, 1.0], [273021.0, 0.0]], field_id=242569.0},
  {id=4077399.0, body=[[273022.0, 0.0], [273023.0, 1.0], [273024.0, 0.0]], field_id=242570.0}
]

6 个答案:

答案 0 :(得分:35)

你告诉Gson它正在寻找一个字符串到对象的列表,它实际上是为了对对象的类型做出最好的猜测。由于JSON doesn't distinguish between integer and floating point fields Gson必须默认使用Float / Double作为数字字段。

Gson从根本上构建用于检查要填充的对象的类型,以确定如何解析数据。如果你没有给它任何暗示,那就不会很好。一种选择是定义一个自定义的JsonDeserializer,但更好的方法是不使用HashMap(绝对不使用Hashtable!),而是给Gson更多关于它期望的数据类型的信息。

class Response {
  int id;
  int field_id;
  ArrayList<ArrayList<Integer>> body; // or whatever type is most apropriate
}

responses = new Gson()
            .fromJson(draft, new TypeToken<ArrayList<Response>>(){}.getType());

同样,Gson的重点是将结构化数据无缝转换为结构化对象。如果你要求它创建一个几乎未定义的结构,比如对象的映射列表,那么你就会击败Gson的整个点,并且可能会使用一些更简单的JSON解析器。

答案 1 :(得分:20)

这有效:

 Gson gson = new GsonBuilder().
        registerTypeAdapter(Double.class,  new JsonSerializer<Double>() {   

    @Override
    public JsonElement serialize(Double src, Type typeOfSrc, JsonSerializationContext context) {
        if(src == src.longValue())
            return new JsonPrimitive(src.longValue());          
        return new JsonPrimitive(src);
    }
 }).create();

答案 2 :(得分:6)

基本上,这个问题没有完美的答案。所有“解决方案”仅适用于某些情况。这是向gson团队报告的一个问题,遗憾的是他们似乎坚持认为“javascript没有整数类型”,好像他们没有意识到gson是针对java而不是javascript。所以他们拒绝修复它直到今天(2018年),尽管像杰克逊这样的其他lib根本没有这样的问题,尽管修复起来容易。因此,您可能必须自己从gson源代码中修复问题并构建自己的gson.jar。源文件为gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java

case NUMBER:
   return in.nextDouble();

答案 3 :(得分:5)

我迟到了,但我自己就碰到了这个。在我的例子中,我不想在我的ArrayList中指定一个Integer类型 - 因为它可以是String或Integer。

我的解决方案如下:

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Double.class,  new JsonSerializer<Double>() {

    public JsonElement serialize(Double src, Type typeOfSrc,
                JsonSerializationContext context) {
            Integer value = (int)Math.round(src);
            return new JsonPrimitive(value);
        }
    });

Gson gs = gsonBuilder.create();

我没有使用Gson gs = new Gson();的默认Gson定义,而是重写了Double.class序列化以返回一个整数。

在我的情况下,我的JSON中有字符串和整数,但我没有任何双打,所以这不会造成问题。

如果你需要double或float值,我想可以添加一些逻辑来测试特定于每种数据类型的属性的值并返回一个合适的值。像

这样的东西
if(/*source has a decimal point*/){
  return new JsonPrimitive(src); 
} else if (/* source has some attribute of a Float */){
  Float value = /*convert the src value from double to a Float */;
  return new JsonPrimitive(value);
} else {
  //it looks like an integer
  Integer value = (int)Math.round(src);
  return new JsonPrimitive(value);
}

我不知道如何测试或转换这些数据类型,但这应该让你走在正确的道路上。

答案 4 :(得分:0)

    fun jsonToMap(json: JSONObject): Map<String, Any> {
        val doubles = Gson().fromJson<Map<String, Any>>(json.toString(), Map::class.java)
        fun doublesToLong(doubles: Map<String, Any>): Map<String, Any> = doubles
                .map { entry ->
                    Pair(entry.key, entry.value.let {
                        when (it) {
                            is Map<*, *> -> doublesToLong(it as Map<String, Any>)
                            is Double -> it.toLong()
                            else -> it
                        }
                    })
                }
                .toMap()
        return doublesToLong(doubles)
    }

答案 5 :(得分:-1)

这对我有用。

步骤1: 将gson中的ObjectTypeAdapter复制到项目中,使其路径与gson中的相同 像这样

com
  - xxx
    - xxx
com
  - google
    - gson
      - internal
        - bind
          ObjectTypeAdapter

步骤2: 修改ObjectTypeAdapter

case NUMBER:
  return in.nextDouble();

修改为

case NUMBER:
  String number = in.nextString();
  try {
    return Long.valueOf(number);
  } catch (NumberFormatException e) {
    return Double.valueOf(number);
  }

好。 Gson将优先考虑项目中的ObjectTypeAdapter。