返回顶部
首页 > 资讯 > 后端开发 > Python >JSON反序列化Long变Integer或Double的问题及解决
  • 451
分享到

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

2024-04-02 19:04:59 451人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

目录一、背景二、研究三、如何解决3.0 将类型写入 JSON 字符串中3.1 提供 POJO 类,慎对 Map<String,Object> 序列化3.2 反序列化自定义

一、背景

工作中可能会遇到对 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.parseObject(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.parser.ParserConfig#getDeserializer 方法获取当前类型的反序列化器为 MapDeserializer

执行其反序列化方法:

com.alibaba.fastjson.parser.deserializer.MapDeserializer#deserialze

在这里插入图片描述

通过 com.alibaba.fastjson.parser.deserializer.MapDeserializer#parseMap 对 Map 类型进行解析。

在这里插入图片描述

由于 Map<String, Object>的 valueType 类型为 Object,因此对 aFloat 使用 JavaObjectDeserializer 反序列化器进行解析。

在这里插入图片描述

跟进 lexer.decimalValue 看下:

在这里插入图片描述

最终通过 com.alibaba.fastjson.parser.JSONScanner#decimalValue 将 aFloat 解析为 BigDecimal 类型。

三、如何解决

3.0 将类型写入 JSON 字符串中

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

在 fastjson 中可以使用 SerializerFeature.WriteClassName

package json;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
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, SerializerFeature.WriteClassName);
        System.out.println(jsonStr);
        // fastjson
        System.out.println("--- fastjson -----");
        Map<String, Object> fastMap = JSON.parseObject(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 -----
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.JsonParseException: Unexpected character ('F' (code 70)): was expecting comma to separate 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.JsonParser._constructError(JsonParser.java:2391)
    at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:735)
    at com.fasterxml.jackson.core.base.ParserMinimalBase._reportUnexpectedChar(ParserMinimalBase.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.Data
public 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 = JSON.parseObject(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.javainuse.com/pojo

在这里插入图片描述

3.3 其他

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

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

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

--结束END--

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

本文链接: https://lsjlt.com/news/162427.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • JSON反序列化Long变Integer或Double的问题及解决
    目录一、背景二、研究三、如何解决3.0 将类型写入 JSON 字符串中3.1 提供 POJO 类,慎对 Map<String,Object> 序列化3.2 反序列化自定义...
    99+
    2024-04-02
  • 如何解决JSON反序列化Long变Integer或Double的问题
    这篇文章主要为大家展示了“如何解决JSON反序列化Long变Integer或Double的问题”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何解决JSON反序列化Long变Integer或Do...
    99+
    2023-06-26
  • JSON序列化导致Long类型被搞成Integer的坑及解决
    目录JSON序列化导致Long类型被搞成Integer的坑上代码方案思考JSON格式转int/long报错Gson转换导致int转换成double的问题JSON序列化导致Long类型...
    99+
    2024-04-02
  • SpringBoot之Json的序列化和反序列化问题怎么解决
    这篇文章主要讲解了“SpringBoot之Json的序列化和反序列化问题怎么解决”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“SpringBoot之Json的序列化和反序列化问题怎么解决”吧...
    99+
    2023-07-02
  • JSON序列化导致Long类型被搞成Integer怎么解决
    本篇内容主要讲解“JSON序列化导致Long类型被搞成Integer怎么解决”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JSON序列化导致Long类型被搞成Integer怎么解决”吧!JSON...
    99+
    2023-06-26
  • Go json反序列化“null“的问题如何解决
    本文小编为大家详细介绍“Go json反序列化“null“的问题如何解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“Go json反序列化“null“的问题如何解决”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入...
    99+
    2023-07-05
  • SpringBoot之Json的序列化和反序列化问题
    目录控制json序列化/反序列化1. @JsonIgnoreProperties的用法2. @JsonProperty 注解3. @JsonCreator 注解4. @JsonSet...
    99+
    2024-04-02
  • Gojson反序列化“null“的问题解决
    目录实验其他测试有这么一段代码,可以先看一下有没有什么问题,作用是输入一段json字符串,反序列化成map,然后将另一个inputMap的内容,merge进这个map func me...
    99+
    2023-03-14
    Go json反序列化 Go json反序列化null
  • 使用Jackson反序列化遇到的问题及解决
    Jackson反序列化遇到的问题 最近在项目中需要使用Jackson把前台转来的字符转为对象,转换过程中发生了错误,报错如下 ​com.fasterxml.jackso...
    99+
    2024-04-02
  • Vue JSON序列化问题怎么解决
    今天小编给大家分享一下Vue JSON序列化问题怎么解决的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。首先,我们需要了解常见...
    99+
    2023-07-06
  • 解决json字符串序列化后的顺序问题
    1、应用场景: 如果项目中用到json字符串转为jsonObject的需求,并且,需要保证字符串的顺序转之前和转成jsonObject之后输出的结果完全一致。可能有点绕口,下面举一个...
    99+
    2024-04-02
  • Java序列化JSON丢失精度问题的解决方法(修复Long类型太长)
    目录原因:解决办法一:解决办法(二):总结Java序列化JSON时long型数值,会出现精度丢失的问题。 原因: java中得long能表示的范围比js中number大,也就意味着部...
    99+
    2024-04-02
  • Java修改Integer变量值遇到的问题及解决
    目录Java 修改Integer变量值我尝试了两种方法去改变Integer的整型值看看源码Integer值比较需要注意的问题原因解决办法Java 修改Integer变量值 对于Int...
    99+
    2024-04-02
  • 如何解决使用Jackson反序列化遇到的问题
    这篇文章主要为大家展示了“如何解决使用Jackson反序列化遇到的问题”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何解决使用Jackson反序列化遇到的问题”这篇文章吧。Jackson反序列...
    99+
    2023-06-20
  • @NonNull导致无法序列化的问题及解决
    目录@NonNull导致无法序列化的问题@NonNull修饰Field反序列化部分值为空分析建议改进总结@NonNull导致无法序列化的问题 以上这个代码在接参的时候报了一个缺少无...
    99+
    2023-01-06
    @NonNull导致无法序列化 @NonNull序列化 @NonNull无法序列化
  • Redis Hash序列化存储的问题及解决方案
    目录SDR序列化方式有多种对Redis的存储设置是我自己写的更改序列化方法更改序列化方式继续使用JdkSerializationRedisSerializer这里说的是Spring Data Redis(一下简称SDR)...
    99+
    2022-11-19
    Redis Hash Hash序列化存储 Redis Hash序列化存储
  • 解决pytorch rnn 变长输入序列的问题
    pytorch实现变长输入的rnn分类 输入数据是长度不固定的序列数据,主要讲解两个部分 1、Data.DataLoader的collate_fn用法,以及按batch进行paddi...
    99+
    2024-04-02
  • java返回json请求中文变成问号的问题及解决
    目录java返回json请求中文变成问号json返回中文全是问号java返回json请求中文变成问号 原来在个人项目时,用layui的数据表格获取数据时,不会出现中文变问号问题 后来...
    99+
    2024-04-02
  • 解决SpringBoot下Redis序列化乱码的问题
    目录SpringBoot下Redis序列化乱码注意问题SpringBoot配置Redis序列化规则,防止乱码下面我们可以编写测试类了具体可以看下图我们需要对它进行配置SpringBo...
    99+
    2024-04-02
  • @Cacheable不起作用的原因以及bean未序列化问题怎么解决
    这篇文章给大家介绍@Cacheable不起作用的原因以及bean未序列化问题怎么解决,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。@Cacheable不起作用的原因:bean未序列化SpringMVC中将service...
    99+
    2023-06-22
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作