防止 Gson 将整数(Integer)表示为浮点数(Float)

1、简介

在将 Java 对象序列化和反序列化为 JSON 格式或从 JSON 格式反序列化 Java 对象时,Google 开发的 Gson 库是一个不错的选择。但是,在序列化对象时,我们通常会遇到 Gson 将整数显示为浮点数的问题。

本文将带你了解为什么在 Gson 的设计中整数被视为浮点数?以及如何解决这个问题。

2、问题的定义

Gson 可将 Java 对象序列化为 JSON。默认情况下,Gson 会将整数序列化为浮点数,以获得更精确的表示。下面是一个简单的例子:

public String jsonString= "[{\"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]]}]";

如上,我们声明了一个名为 jsonString 的 JSON 字符串,它代表一个对象数组。这个 JSON 数组有不同的字段,如 idfield_idbody

现在,我们使用 Gson 库将 JSON 字符串反序列化为 Hashtable<String, Object> 对象列表。

ArrayList<Hashtable<String, Object>> responses;

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

responses = new Gson().fromJson(jsonString, ResponseList);

如上,我们声明了一个名为 responsesArrayList,其中包含 Key 为字符串、Value 为对象的 Hashtable 类型元素。此外,我们还利用 Gson 库将 jsonString 反序列化为 Hashtables 列表。

最后,我们在反序列化过程中使用 TypeToken 获取泛型类型信息。

格式化后的 responses 如下:

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

注意,Gson 将整数表示为浮点数。

3、Gson 的默认数字策略(Number Strategy)

Gson 的默认数字策略旨在在表示数值时在准确性和灵活性之间取得平衡。将整数使用浮点数来表示的决策基于 JSON 缺乏明确支持区分整数和浮点数类型的能力。因此,Gson 选择了默认策略,以确保数值的精度保持不变。

但是,这种默认行为可能不符合特定要求或偏好,尤其是在处理 JSON 表示法中整数应保持整数的情况时。

4、使用 setObjectToNumberStrategy() 方法

利用 Gson 的 setObjectToNumberStrategy() 方法,我们可以在反序列化过程中彻底控制对象到数字转换机制的功能。

让我们通过一个示例来了解这种功能:

public static String expectedOutput ="[{body=, field_id=242566, id=4077395}, " +
  "{body=[[273019, 0], [273020, 1], [273021, 0]], field_id=242569, id=4077398}, " +
  "{body=[[273022, 0], [273023, 1], [273024, 0]], field_id=242570, id=4077399}]";

@Test
public void givenJsonString_whenUsingsetObjectToNumberStrategyMethod_thenValidateOutput() {
    Gson gson = new GsonBuilder()
      .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
      .create();
    ArrayList<Hashtable<String, Object>> responses = gson.fromJson(jsonString,
      new TypeToken<ArrayList<Hashtable<String, Object>>>() {}.getType());

    assertEquals(expectedOutput, responses.toString());
}

如上,通过使用 setObjectToNumberStrategy() 方法,我们可以为 Gson 设置 ToNumberPolicy.LONG_OR_DOUBLE 等策略,以指定其处理数值的行为。最后,使用 assertEquals() 方法验证转换过程。

此外,Gson 的 ToNumberPolicy 枚举支持多种处理数值的策略。除了我们在示例中使用的 ToNumberPolicy.LONG_OR_DOUBLE 之外,其他策略包括:

  • ToNumberPolicy.DOUBLE_ONLY: 在反序列化过程中将所有数值转换为 double。
  • ToNumberPolicy.LONG_ONLY: 在反序列化过程中将所有数值转换为 long
  • ToNumberPolicy.DEFAULT: 保留 Gson 的默认行为,将整数表示为浮点数

5、总结

本文介绍了使用 Gson 时遇到的一个问题:在序列化过程中,整数会自动转换为浮点数,以及如何通过 setObjectToNumberStrategy() 方法来自定义数值的序列化和反序列化策略。


Ref:https://www.baeldung.com/java-gson-prevent-expressing-integers-as-floats