揭祕前端精度丟失之謎!!!

今天測試突然跟我說頁面顯示的數值和數據庫的對應不上,一開始我以爲是程序問題,把數據給修改了,但是後面檢查程序發現,沒有任務問題,用postman請求,發現數據也和數據庫的一致。但是頁面上面顯示的就是不一樣。

問題追蹤

一開始我懷疑是前端轉型把精度丟失,前端那邊直接是通過Number接收的,沒有任何轉型操作。後來我懷疑可能是因爲服務器是Long類型,前端是Number類型,可能是精度對應不上,後面一查資料發現還真是這個問題。

Number和Long精度

Number的最大長度是2的53次,也就是9007199254740992,而Long的最大長度是2的64次,也就是9223372036854775807。所以會出現精度丟失的情況。

解決方案

要解決這個問題其實不難,只要將Long類型變爲String類型即可,但是我們不可能直接將字段修改爲String,這樣會涉及很多模塊代碼的修改,工作量和安全性都會受到影響。

還有一種辦法是重新再添加一個新的字段,然後get的時候將需要的字段轉爲字符串返回即可,如下所示:

    private Long userId;
    private String userIdStr;
    public String getUserIdStr() {
        return this.userId+"";
    }
    public void setUserIdStr(String userIdStr) {
        this.userIdStr = userIdStr;
    }

但是這樣一方面需要修改dto對象的字段,另一方面前端也需要相應的修改,如果有很多地方需要這樣做,那工作量也是非常大的,所以這種方式也是不可取的。

方案篩選

最理想的方案應該是不修改字段任何屬性,因爲這樣前端就不需要變動。(如果前端指定具體接收類型,那就需要修改),在序列化返回給前端的時候將Long類型的字段轉爲String類型。

這邊就給大家介紹一下jackson是如何進行序列化和反序列化的。

首先我們來看一下沒有進行任何序列化修改,Long類型怎麼輸出的?我們先來看一下代碼。

@RequestMapping(value = "testDemo", method = RequestMethod.GET)
    @ResponseBody
    public TestDto testDemo() {
        TestDto testDto = new TestDto();
        testDto.setId(Long.valueOf("123456789012345678"));
        return testDto;
    }
    
@Data
public class TestDto {
    /**
     * ID標識
     */
    private Long id;
}

接下來我們用postman請求一下,看看返回的結果是啥樣的?

{
    "id": 123456789012345678
}

我們可以看到String類型的返回結果是沒有雙引號或者單引號的,我們修改一下id字段的類型(從Long類型修改爲String),看看輸出結果有什麼不一樣。

@Data
public class TestDto {
    /**
     * ID標識
     */
    private String id;
}

輸出結果:

{
    "id": "123456789012345678"
}

我們可以很明顯的看到,字段id值是有雙引號的。那我們如何在不修改任何代碼的情況下,返回值值從Long類型修改爲Stirng,返回給前端。

接下來就是重頭戲了,我們先寫一個Annotation註解,如下所示:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@JacksonAnnotationsInside
@JsonSerialize(using = LongToStringSerializer.class)
public @interface LongToString {
}

不懂@Retention和@Target含義的童鞋,可以自行百度。

重點在LongToStringSerializer類中,該類的作用是進行json序列化,它有一個serialize方法,允許我們對序列化的字段進行修改,具體我們來看一下代碼:

public class LongToStringSerializer extends JsonSerializer<Object> {
    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeString(String.valueOf(value));
    }
}

這邊我們將原來是Long類型的字段,重寫爲String類型,最後我們用postman來測試一下,返回結果是否包含雙引號。

{
    "id": "123456789012345678"
}

從結果我們可以很明顯的看出,id字段的類型是String,期間我們沒有修改id字段的類型,但是通過jackson序列化的時候進行字段類型重寫,就可以輕輕鬆鬆實現類型的修改。

總結

用法雖然很簡單,但是這其中過程是怎麼樣的呢,到底在什麼時候會進行對象序列化輸出?我們下一節來具體給大家介紹一下。今天文章分享就到這邊,謝謝童鞋們的閱讀~

想要更多幹貨、技術猛料的孩子,快點拿起手機掃碼關注我,我在這裏等你哦~

林老師帶你學編程https://wolzq.com

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章