81

当我尝试将字符串转换为 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}
]
4

10 回答 10

48

您告诉 Gson 它正在寻找字符串到对象的映射列表,这实质上是为了对对象的类型做出最佳猜测。由于JSON 不区分整数和浮点字段,因此 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 解析器。

于 2013-03-19T19:04:55.623 回答
40

这有效:

 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();
于 2013-12-27T15:40:30.813 回答
13

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

case NUMBER:
   return in.nextDouble();
于 2018-03-28T08:40:24.370 回答
8

该库从 2.8.9 版本开始提供了一个解决方案。

我们可以使用 setObjectToNumberStrategy 设置如何将 Object 转换为数字

在这种情况下,LONG_OR_DOUBLE 的实现将起作用。可以用作

GsonBuilder()
    .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
    .create()

详情请参考以下链接

https://github.com/google/gson/pull/1290

于 2021-12-04T20:29:35.717 回答
7

我参加聚会迟到了,但我自己也遇到了。就我而言,我不想在我的 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 定义Gson gs = new Gson();,而是重写了 Double.class 序列化以返回一个整数。

就我而言,我的 JSON 中有字符串和整数,但我没有任何双精度数,所以这不会造成问题。

如果您需要双精度或浮点值,我想可以添加一些逻辑来测试每种数据类型特定属性的值并返回适当的值。就像是

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);
}

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

于 2013-12-11T16:10:31.960 回答
1

这对我有用。

第一步:将gson中的ObjectTypeAdapter复制到项目中,保持路径和gson中的一样这样

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

第二步:修改 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。

于 2019-09-09T02:22:59.467 回答
1

Kotlin 中的自定义序列化器解决方案,这有点棘手,因为您必须区分 java.lang.Double 和 Double (kotlin.Double)。

private val Gson: Gson = GsonBuilder().registerTypeAdapter(java.lang.Double::class.java, object : JsonSerializer<Double> {
    override fun serialize(src: Double, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
        return if (src == src.toLong().toDouble()) JsonPrimitive(src.toLong()) else JsonPrimitive(src)
    }
}).create()
于 2021-12-02T01:05:13.443 回答
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)
    }
于 2020-07-10T15:07:06.503 回答
0

使用杰克逊

    public static Map<String, Object> jsonToMap(final String jsonString) {
    try {
        final ObjectMapper objectMapper = new ObjectMapper();
        return objectMapper.convertValue(objectMapper.readTree(jsonString), new TypeReference<Map<String, Object>>() {
        });
    } catch (final Exception e) {
        throw new InternalServiceException("lol");
    }
}
于 2021-12-16T10:23:54.887 回答
0

谷歌修复问题https://github.com/google/gson/commit/fe30b85224316cabf19f5dd3223843437c297802#diff-9bf510cca1fa5b32b008e7daa417abf15602571dbc87f5436d9f3558ded492a5 请将gson版本更新为2.8.9

于 2022-01-06T07:09:53.100 回答