[英]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.