繁体   English   中英

为什么 Gson 将 Integer 解析为 Double?

[英]Why does Gson parse an Integer as a Double?

一个复杂的 json 字符串,我想将它转换为 map,我遇到了问题。

请看这个简单的测试:

public class Test {

    @SuppressWarnings("serial")
    public static void main(String[] args) {
        Map<String, Object> hashMap = new HashMap<String, Object>();
        hashMap.put("data", "{\"rowNum\":0,\"colNum\":2,\"text\":\"math\"}");

        Map<String,Object> dataMap = JsonUtil.getGson().fromJson(
                hashMap.get("data").toString(),new TypeToken<Map<String,Object>>() {}.getType());

        System.out.println(dataMap.toString());

    }
}

结果:
控制台打印: {rowNum=0.0, colNum=2.0, text=math}
Int转Double;
为什么 gson 会更改类型,我该如何解决?

Gson是一个简单的解析器。 如果要将数据解析为Object它将始终使用Double作为默认数字类型。

检查此问题以获取更多信息: 如何防止Gson将整数表示为浮点数

我建议您使用Jackson Mapper 即使您解析到对象,Jackson也可以区分类型:

  • "2"Integer
  • "2.0"Double

这是一个例子:

Map<String, Object> hashMap = new HashMap<String, Object>();
hashMap.put("data", "{\"rowNum\":0,\"colNum\":2,\"text\":\"math\"}");
ObjectMapper mapper = new ObjectMapper();
TypeReference<HashMap<String, Object>> typeRef = new TypeReference<HashMap<String, Object>>() {};

HashMap<String, Object> o = mapper.readValue(hashMap.get("data").toString(), typeRef);

专家

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.9.0</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.0</version>
</dependency>

JSON不会像Java一样区分不同类型的数字。 它会将所有数字视为一种类型。

将数字解析为Double是Gson库的实现细节。 遇到JSON数字时,默认将其解析为Double

与其使用Map ,不如定义一个封装了JSON结构所有字段的POJO更好。 这使得以后访问数据变得更加容易,并且数字将自动解析为Integer

class Cell {
    private Integer rowNum;
    private Integer colNum;
    private String text;
}

public static void main(String[] args) throws Exception {
    Map<String, Object> hashMap = new HashMap<String, Object>();
    hashMap.put("data", "{\"rowNum\":0,\"colNum\":2,\"text\":\"math\"}");

    Cell cell = new Gson().fromJson(hashMap.get("data").toString(), Cell.class);
    System.out.println(cell);
}

我也需要让它工作。 当从 JSON 字符串反序列化为 Map 时,GSON 这样做是有道理的,因为 Double 是数字的最大容器。

在我的例子中,如果类型发生变化,那将是非常不方便的,所以我实现了可以被认为是 hack 的东西,但它确实有效,所以它就是这样。

起点是 function 以获取 JSON 字符串并猜测它是列表、Map 还是普通字符串

public static Object parseJson(String json) {
    if (json == null || json.isBlank()) {
        return null;
    }

    var input = json.trim();
    if (input.startsWith("[")) {
        var list = GSON.fromJson(input, List.class);
        for (Object o : list) {
            if (o instanceof Map) {
                visit((Map)o);
            }
        }
        return list;
    } else if (input.startsWith("{")) {
        var map = GSON.fromJson(input, Map.class);
        visit(map);
        return map;
    } else {
        return GSON.fromJson(input, String.class);
    }
}

然后我实现了一个访客模式(在这种情况下我使用了 Java 11)。 它是一个递归的 function,它访问 map 中的每个属性,如果该属性是 Iterable 或另一个 Map,它也将访问这些属性。

public static void visit(Map<String, Object> map) {
    for (var entry : map.entrySet()) {
        var value = entry.getValue();
        if (value == null) {
            continue;
        }
        if (value instanceof Map) {
            visit((Map<String, Object>)value);
        }
        if (value instanceof Iterable) {
            var it = (Iterable)value;
            visit(it, o -> {
                if (o instanceof Map) {
                    visit((Map<String, Object>)o);
                }
            });
        }
        if (value instanceof Double) {
            var d = (Double) value;
            var bigDecimal = new BigDecimal(d);
            long longValue = bigDecimal.longValue();
            var integerPart = String.valueOf(longValue);
            var decimalPart = String.valueOf(bigDecimal.subtract(
                    new BigDecimal(longValue)));
            if (decimalPart.matches("^0+$")) {
                if (longValue > Integer.MAX_VALUE || longValue < Integer.MIN_VALUE) {
                    entry.setValue(d.longValue());
                } else {
                    entry.setValue(d.intValue());
                }
            }
        }
    }
}

public static void visit(Iterable it, Consumer consumer) {
    it.forEach(o -> {
        if (o instanceof Iterable) {
            visit((Iterable) o, consumer);
        } else {
            consumer.accept(o);
        }
    });
}

每次遇到 Double 类型时,它都会尝试确定它是否有小数点。 如果它有一个,它仍然是一个双。 否则,它通过分别比较 Integer.MAX_VALUE 和 Integer.MIN_VALUE 来检查 integer 部分应该是 Long 还是 Integer 类型。

如果检测到类型更改,则 map 条目值将替换为新的原始类型。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM