首页 > 作文

JSON反序列化Long变Integer或Double的问题及解决

更新时间:2023-04-04 14:27:03 阅读: 评论:0

目录
一、背景二、研究三、如何解决3.0 将类型写入 json 字符串中3.1 提供 pojo 类,慎对 map<string,object> 序列化3.2 反序列化自定义类3.3 其他

一、背景

工作中可能会遇到对 map<string,object> 进行 json 序列化,其中值中包含 long 类型的数据,反序列化后强转 long 时报类型转换异常的问题。

本文简单探讨下该问题,并给出解决方案,如果你想直接看建议,直接翻到第三部分即可。

二、研究

本文主要以 jackson、 gson、fastjson 三个库为例,版本分别如下:

   <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->        <dependency>            <groupid>com.fasterxml.jackson.core</groupid>            <artifactid>jackson-core</artifactid>            <version>2.13.0</version>        </dependency>        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->        <dependency>            <groupid>com.fasterxml.jackson.core</groupid>            <artifactid>jackson-databind</artifactid>            <version>2.13.0</version>        </dependency>        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->        <dependency>            <groupid>com.alibaba</groupid>            <artifactid>fastjson</artifactid>            <version>1.2.78</version>        </dependency>        <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->        <dependency>            <groupid>com.google.code.gson</groupid>            <artifactid>gson</artifactid>            <version>2.8.8</version>        </dependency>   

代码示例

package json;import com.alibaba.fastjson.json;import com.fasterxml.jackson.core.jsonprocessingexception;import com.fasterxml.jackson.core.type.typereference;import com.fasterxml.jackson.databind.objectmapper;import com.google.gson.gsonbuilder;import java.util.hashmap;import java.util.map;public class objectdemo {    public static void main(string[] args) throws jsonprocessingexception {        map<string, object> datamap = new hashmap<>(2);        datamap.put("ainteger", 1);        datamap.put("along", 2l);        string jsonstr = json.tojsonstring(datamap);        system.out.println(jsonstr);        // fastjson        system.out.println("--- fastjson -----");        map<string, object> fastmap = json.parobject(jsonstr, new com.alibaba.fastjson.typereference<map<string, object>>() {        });        printmap(fastmap);        system.out.println("--- gson -----");        map<string, object> gsonmap = new gsonbuilder().create()                .fromjson(jsonstr, (new typereference<map<string, object>>(){}).gettype() );        printmap(gsonmap);        system.out.println("--- jackson -----");        objectmapper objectmapper = new objectmapper();        map<string, object> jacksonmap = objectmapper.readvalue(jsonstr, new typereference<map<string, object>>() {        });        printmap(jacksonmap);    }    private static void printmap(map<string, object> map) {        map.foreach((key, value) -> {            system.out.println("key:" + key + ",value=" + value + ",valueclass=" + value.getclass());        });    }}

运行结果:

{“ainteger”:1,”along”:2}
— fastjson —–
key:along,value=2,valueclass=class java.lang.integer
key:ainteger,value=1,valueclass=class java.lang.integer
— gson —–
key:ainteger,value=1.0,valueclass=class java.lang.double
key:along,value=2.0,valueclass=class java.lang.double
— jackson —–
key:ainteger,value=1,valueclass=class java.lang.integer
key:along,value=2,valueclass=class java.lang.integer

along 虽然原始类型为 long 但是 fastjson 和 jackson 中被反序列化为 integer 类型,gson 中被映射为 double 类型。

我们观察序列化后的 json 字符串:

{"ainteger":1,"along":2}

会发现其实 json 中并没有包含类型信息,而反序列化的类型为 map.class 或者 map<string,object> 类型,当你只知道这些信息时,你无法得知 along 原始类型为 long 。

因此不同的json 序列化工具给出了自己的默认处理行为。

当我们把 along 的值调整到 超过 (integer.max_value,long.max_value] 的范围之间时,fastjson 和 jackson 可以解析为 long 类型。

 map<string, object> datamap = new hashmap<>(2);        datamap.put("ainteger", 1);        datamap.put("along", long.max_value);

输出的结果:

{“ainteger”:1,”along”:9223372036854775807}
— fastjson —–
key:along,value=9223372036854775807,valueclass=class java.lang.long
key:ainteger,value=1,valueclass=class java.lang.integer
— gson —–
key:ainteger,value=1.0,valueclass=class java.lang.double
key:along,value=9.223372036854776e18,valueclass=class java.lang.double
— jackson —–
key:ainteger,value=1,valueclass=class java.lang.integer
key:along,value=9223372036854775807,valueclass=class java.lang.long

我们大致了解到, fastjson 和 jackson 默认情况下整数类型优先选取 integer ,超过 integer 范围再选择 long ,以此类推。

而当我们放入 float 类型时,结果又有差异:

   map<string, object> datamap = new hashmap<>(2);        datamap.put("ainteger", 1);        datamap.put("afloat", 0.1f);

运行结果:

{“ainteger”:1,”afloat”:0.1}
— fastjson —–
key:ainteger,value=1,valueclass=class java.lang.integer
key:afloat,value=0.1,valueclass=class java.math.bigdecimal
—落红是什么 gson —–
key:ainteger,value=1.0,valueclass=class java.lang.double
key:afloat,value=0.1,valueclass=class java.lang.double
— jackson —–
key:ainteger,value=1,valueclass=class java.lang.integer
key:afloat,value=0.1,valueclass=class java.lang.double

fastjson 中 float 被解析为 bigdecimal, gson 和 jackson 中被解析为 double 类型。

具体底层如何处理,大家可以对每个框架的反序列方法单步跟进去即可得到答案。

这里以 fastjson 为例,简单调试下:

fastjson 底通过 com.alibaba.fastjson.parr.parrconfig#getderializer 方法获取当前类型的反序列化器为 mapderializer

执行其反序列化方法:

com.alibaba.fastjson.parr.derializer.mapderializer#derialze

通过 com.alibaba.fastjson.parr.derializer.mapderializer#parmap 对 map 类型进行解析。

由于 map<string, object>的 valuetype 类型为 object,因此对 afloat 使用 javaobjectderializer 反序列化器进行解析。

跟进 lexer.decimalvalue 看下:

最终通过 com.alibaba.fastjson.parr.jsonscanner#decimalvalue 将 afloat 解析为 bigdecimal 类型。

三、如何解决

3.0 将类型写入 json 字符串中

如果我们能将原始类型写入到 json 字符串中,那么反序列化时自然就可以复原原始的类型。

在 fastjson 中可以使用 rializerfeature.writeclassname

package json;import com.alibaba.fastjson.json;import com.alibaba.fastjson.rializer.rializerfeature;import java.util.hashmap;import java.util.map;public class jsondemo {    public static void main(string[] args) {        map<string, object> datamap = new hashmap<>(2);        datamap.put("ainteger", 1);        datamap.put("along", 2l);        datamap.put("afloat", 3f);        string jsonstr = json.tojsonstring(datamap, rializerfeature.writeclassname);        syst万圣节化妆舞会em.out.println(jsonstr);        // fastjson        system.out.println("--- fastjson -----");        map<string, object> fastmap = json.parobject(jsonstr, new com.alibaba.fastjson.typereference<map<string, object>>() {        });        printmap(fastmap);    }    private static void printmap(map<string, object> map) {        map.foreach((key, value) -> {            system.out.println("key:" + key + ",value=" + value + ",valueclass=" + value.getclass());        });    }}

打印的结果

{“@type”:”java.util.hashmap”,”afloat”:3.0f,”ainteger”:1,”along”:2l}
— fastjson &西安交大城市学院#8212;–
key:along,value=2,valueclass=class java.lang.long
key:afloat,value=3.0,valueclass=class java.lang.float
key:ainteger,value=1,valueclass=class java.lang.integer

虽然,这种方法可以解决问题,但是这也通常要求序列化和反序列化使用同一个 json 工具

比如上面的 {“@type”:”java.util.hashmap”,”afloat”:3.0f,”ainteger”:1,”along”:2l} 直接使用 jackson 进行反序列化会报错:

system.out.println("--- jackson -----");    objectmapper objectmapper = new objectmapper();    map<string, object> jacksonmap = objectmapper.readvalue(jsonstr, new typereference<map<string, object>>() {    });    printmap(jacksonmap);

报错内容:

— jackson —–
exception in thread “main” com.fasterxml.jackson.core.jsonparexception: unexpected character (‘f’ (code 70)): was expecting comma to parate object entries
at [source: (string)”{“@type”:”java.util.hashmap”,”afloat”:3.0f,”ainteger”:1,”along”:2l}”; line: 1, column: 43]
at com.fasterxml.jackson.core.jsonparr._constructerror(jsonparr.java:2391)
at com.fasterxml.jackson.core.ba.parrminimalba._reporterro吃什么可以全身美白r(parrminimalba.java:735)
at com.fasterxml.jackson.core.ba.parrminimalba._reportunexpectedchar(parrminimalba.java:659)

3.1 提供 pojo 类,慎对 map<string,object> 序列化

强烈建议不要怕麻烦,直接定义 pojo 类。

不仅不受 json 框架的约束,而且对方解析时也非常明确,不容易出错。

如工作中在发送mq 消息时很多人图方便,不想定义pojo 对象,因为这样通常需要打包比较麻烦,就将要传输给其他系统的数据定义为 map 类型,下游再根据 key 去解析,这是一个非常不好的习惯。

很容易造成上下游类型不一致,造成更换 json 反序列化工具时出现故障。

因此发送 mq 消息时,最好给出相应的 pojo 类。

实际工作中,还遇到有同学将 map<string,object> 使用 json 序列化的方式存储到 redis 中,然后反序列化后,将原本 long 类型的值,强转为 long 导致线上出现bug(前面讲到,这种情况下使用 fastjson 时,如果值小于整数最大值,反序列化为 integer 类型,强转必然会报错)。

3.2 反序列化自定义类

如果上游序列化是 map<string,object>, 如果类型核实清楚,我们依然可以自定义 pojo 类来反序列化。

@lombok.datapublic class data {    private float afloat;    private integer ainteger;}
  map<string, object> datamap = new hashmap<>(2);        datamap.put("ainteger", 1);        datamap.put("afloat", 0.1f);        string jsonstr = json.tojsonstring(datamap);        data data = js报幕词on.parobject(jsonstr, data.class);        system.out.println(data);

输出结果:

data(afloat=0.1, ainteger=1)

可能有些同学会觉得定义 pojo 类很麻烦,其实我们可以使用 idea 插件或者在线工具实现 json 字符串生成 pojo 类。

如 json2pojo idea 插件

和一些在线生成工具:

https://json2csharp.com/json-to-pojo

https://www.javainu.com/pojo

3.3 其他

可能网上还会有其他解决方案,比如自定义序列化和反序列化器。

我个人不太建议这么做,因为这样不够通用,跨系统使用不太方便。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持www.887551.com。

本文发布于:2023-04-04 14:26:52,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/zuowen/3ee59fb2cdcd3811fb03ef6ccd9c932e.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

本文word下载地址:JSON反序列化Long变Integer或Double的问题及解决.doc

本文 PDF 下载地址:JSON反序列化Long变Integer或Double的问题及解决.pdf

标签:类型   序列化   字符串   自定义
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图