最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JSON反序列化Long變Integer或Double的問(wèn)題及解決

 更新時(shí)間:2022年01月13日 16:08:12   作者:明明如月學(xué)長(zhǎng)  
這篇文章主要介紹了JSON反序列化Long變Integer或Double的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

一、背景

工作中可能會(huì)遇到對(duì) Map<String,Object> 進(jìn)行 JSON 序列化,其中值中包含 Long 類型的數(shù)據(jù),反序列化后強(qiáng)轉(zhuǎn) Long 時(shí)報(bào)類型轉(zhuǎn)換異常的問(wèn)題。

本文簡(jiǎn)單探討下該問(wèn)題,并給出解決方案,如果你想直接看建議,直接翻到第三部分即可。

二、研究

本文主要以 jackson、 gson、fastjson 三個(gè)庫(kù)為例,版本分別如下:

   <!-- 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());
        });
    }
}

運(yùn)行結(jié)果:

{"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}

會(huì)發(fā)現(xiàn)其實(shí) JSON 中并沒(méi)有包含類型信息,而反序列化的類型為 Map.class 或者 Map<String,Object> 類型,當(dāng)你只知道這些信息時(shí),你無(wú)法得知 aLong 原始類型為 Long 。

因此不同的JSON 序列化工具給出了自己的默認(rèn)處理行為。

當(dāng)我們把 aLong 的值調(diào)整到 超過(guò) (Integer.MAX_VALUE,Long.MAX_VALUE] 的范圍之間時(shí),fastjson 和 jackson 可以解析為 Long 類型。

 Map<String, Object> dataMap = new HashMap<>(2);
        dataMap.put("aInteger", 1);
        dataMap.put("aLong", Long.MAX_VALUE);

輸出的結(jié)果:

{"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 默認(rèn)情況下整數(shù)類型優(yōu)先選取 Integer ,超過(guò) Integer 范圍再選擇 Long ,以此類推。

而當(dāng)我們放入 Float 類型時(shí),結(jié)果又有差異:

   Map<String, Object> dataMap = new HashMap<>(2);
        dataMap.put("aInteger", 1);
        dataMap.put("aFLoat", 0.1F);

運(yùn)行結(jié)果:

{"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 類型。

具體底層如何處理,大家可以對(duì)每個(gè)框架的反序列方法單步跟進(jìn)去即可得到答案。

這里以 fastjson 為例,簡(jiǎn)單調(diào)試下:

fastjson 底通過(guò) com.alibaba.fastjson.parser.ParserConfig#getDeserializer 方法獲取當(dāng)前類型的反序列化器為 MapDeserializer

執(zhí)行其反序列化方法:

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

在這里插入圖片描述

通過(guò) com.alibaba.fastjson.parser.deserializer.MapDeserializer#parseMap 對(duì) Map 類型進(jìn)行解析。

在這里插入圖片描述

由于 Map<String, Object>的 valueType 類型為 Object,因此對(duì) aFloat 使用 JavaObjectDeserializer 反序列化器進(jìn)行解析。

在這里插入圖片描述

跟進(jìn) lexer.decimalValue 看下:

在這里插入圖片描述

最終通過(guò) com.alibaba.fastjson.parser.JSONScanner#decimalValue 將 aFloat 解析為 BigDecimal 類型。

三、如何解決

3.0 將類型寫入 JSON 字符串中

如果我們能將原始類型寫入到 JSON 字符串中,那么反序列化時(shí)自然就可以復(fù)原原始的類型。

在 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());
        });
    }
}

打印的結(jié)果

{"@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

雖然,這種方法可以解決問(wèn)題,但是這也通常要求序列化和反序列化使用同一個(gè) JSON 工具

比如上面的 {"@type":"java.util.HashMap","aFloat":3.0F,"aInteger":1,"aLong":2L} 直接使用 jackson 進(jìn)行反序列化會(huì)報(bào)錯(cuò):

?System.out.println("--- jackson -----");
? ? ? ? ObjectMapper objectMapper = new ObjectMapper();
? ? ? ? Map<String, Object> jacksonMap = objectMapper.readValue(jsonStr, new TypeReference<Map<String, Object>>() {
? ? ? ? });
? ? ? ? printMap(jacksonMap);

報(bào)錯(cuò)內(nèi)容:

--- 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 類,慎對(duì) Map<String,Object> 序列化

強(qiáng)烈建議不要怕麻煩,直接定義 POJO 類。

不僅不受 JSON 框架的約束,而且對(duì)方解析時(shí)也非常明確,不容易出錯(cuò)。

如工作中在發(fā)送MQ 消息時(shí)很多人圖方便,不想定義POJO 對(duì)象,因?yàn)檫@樣通常需要打包比較麻煩,就將要傳輸給其他系統(tǒng)的數(shù)據(jù)定義為 Map 類型,下游再根據(jù) key 去解析,這是一個(gè)非常不好的習(xí)慣。

很容易造成上下游類型不一致,造成更換 JSON 反序列化工具時(shí)出現(xiàn)故障。

因此發(fā)送 MQ 消息時(shí),最好給出相應(yīng)的 POJO 類。

實(shí)際工作中,還遇到有同學(xué)將 Map<String,Object> 使用 JSON 序列化的方式存儲(chǔ)到 Redis 中,然后反序列化后,將原本 Long 類型的值,強(qiáng)轉(zhuǎn)為 Long 導(dǎo)致線上出現(xiàn)BUG(前面講到,這種情況下使用 fastjson 時(shí),如果值小于整數(shù)最大值,反序列化為 Integer 類型,強(qiáng)轉(zhuǎn)必然會(huì)報(bào)錯(cuò))。

3.2 反序列化自定義類

如果上游序列化是 Map<String,Object>, 如果類型核實(shí)清楚,我們依然可以自定義 POJO 類來(lái)反序列化。

@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);

輸出結(jié)果:

Data(aFloat=0.1, aInteger=1)

可能有些同學(xué)會(huì)覺(jué)得定義 POJO 類很麻煩,其實(shí)我們可以使用 IDEA 插件或者在線工具實(shí)現(xiàn) JSON 字符串生成 POJO 類。

如 Json2Pojo IDEA 插件

和一些在線生成工具:

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

在這里插入圖片描述

https://www.javainuse.com/pojo

在這里插入圖片描述

3.3 其他

可能網(wǎng)上還會(huì)有其他解決方案,比如自定義序列化和反序列化器。

我個(gè)人不太建議這么做,因?yàn)檫@樣不夠通用,跨系統(tǒng)使用不太方便。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • spring redis 如何實(shí)現(xiàn)模糊查找key

    spring redis 如何實(shí)現(xiàn)模糊查找key

    這篇文章主要介紹了spring redis 如何實(shí)現(xiàn)模糊查找key的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • mybatis映射表結(jié)構(gòu)的使用

    mybatis映射表結(jié)構(gòu)的使用

    MyBatis通過(guò)表結(jié)構(gòu)映射實(shí)現(xiàn)數(shù)據(jù)庫(kù)操作,本文就來(lái)介紹一下mybatis映射表結(jié)構(gòu)的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-11-11
  • Java插入JSON對(duì)象到PostgreSQL的步驟詳解

    Java插入JSON對(duì)象到PostgreSQL的步驟詳解

    在現(xiàn)代軟件開(kāi)發(fā)中,由于?JSON?數(shù)據(jù)的輕量和通用性,處理?JSON?數(shù)據(jù)已經(jīng)變得無(wú)處不在,PostgreSQL?憑借其對(duì)?JSON?的強(qiáng)大支持,為存儲(chǔ)和查詢?JSON?數(shù)據(jù)提供了出色的平臺(tái),本文給大家介紹了Java插入JSON對(duì)象到PostgreSQL的步驟,需要的朋友可以參考下
    2024-11-11
  • Java如何使用ConfigurationProperties獲取yml中的配置

    Java如何使用ConfigurationProperties獲取yml中的配置

    這篇文章主要介紹了Java如何使用ConfigurationProperties獲取yml中的配置,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • 淺談一下RabbitMQ、Kafka和RocketMQ消息中間件對(duì)比

    淺談一下RabbitMQ、Kafka和RocketMQ消息中間件對(duì)比

    這篇文章主要介紹了淺談一下RabbitMQ、Kafka和RocketMQ消息中間件對(duì)比,消息中間件屬于分布式系統(tǒng)中一個(gè)字系統(tǒng),關(guān)注于數(shù)據(jù)的發(fā)送和接收,利用高效可靠的異步信息傳遞機(jī)制對(duì)分布式系統(tǒng)中的其余各個(gè)子系統(tǒng)進(jìn)行集成,需要的朋友可以參考下
    2023-05-05
  • Java前后端分離項(xiàng)目跨域問(wèn)題解決方案

    Java前后端分離項(xiàng)目跨域問(wèn)題解決方案

    本文主要介紹了Java前后端分離項(xiàng)目跨域問(wèn)題解決方案,其中后端基于SpringBoot,前端使用了jQuery、axios等框架,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03
  • idea +junit單元測(cè)試獲取不到bean注入的解決方式

    idea +junit單元測(cè)試獲取不到bean注入的解決方式

    這篇文章主要介紹了idea +junit單元測(cè)試獲取不到bean注入的解決方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-08-08
  • java利用遞歸實(shí)現(xiàn)類別樹(shù)示例代碼

    java利用遞歸實(shí)現(xiàn)類別樹(shù)示例代碼

    這篇文章主要給大家介紹了關(guān)于java利用遞歸實(shí)現(xiàn)類別樹(shù)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • Springboot Druid 自定義加密數(shù)據(jù)庫(kù)密碼的幾種方案

    Springboot Druid 自定義加密數(shù)據(jù)庫(kù)密碼的幾種方案

    這篇文章主要介紹了Springboot Druid 自定義加密數(shù)據(jù)庫(kù)密碼的步驟,幫助大家更好的理解和使用springboot,感興趣的朋友可以了解下
    2020-12-12
  • Java中Comparable與Comparator的區(qū)別解析

    Java中Comparable與Comparator的區(qū)別解析

    這篇文章主要介紹了Java中Comparable與Comparator的區(qū)別解析,實(shí)現(xiàn)Comparable接口,重寫compareTo方法,一般在實(shí)體類定義的時(shí)候就可以選擇實(shí)現(xiàn)該接口,提供一個(gè)默認(rèn)的排序方式,供Arrays.sort和Collections.sort使用,需要的朋友可以參考下
    2024-01-01

最新評(píng)論

柏乡县| 台北县| 乐清市| 安新县| 荔浦县| 淅川县| 信丰县| 水城县| 内黄县| 义马市| 南溪县| 舞钢市| 垣曲县| 通道| 轮台县| 两当县| 新闻| 阿克| 凤翔县| 泾川县| 南木林县| 绥中县| 类乌齐县| 梁河县| 长岛县| 郧西县| 三河市| 宣汉县| 迁西县| 潍坊市| 大新县| 德保县| 凤阳县| 老河口市| 青海省| 宁陵县| 青田县| 吴忠市| 江城| 会理县| 绥滨县|