Jison解決JS處理後端返回的Long型資料精度丟失問題

HSvng發表於2019-01-30

在前端頁面展示資料的時候,通常都需要處理來自後端的json資料。通常這個過程都是非常簡單的,比如通過jQuery的ajax。但是如果伺服器傳來的json中包含一個很大的整數,如 { "id": 296675198462066688 } ,那麼接受後會發現變成了 { id: 296675198462066700 } 。

簡單的解決方法,讓後端傳給你string型別
但是後端的人會說:你們前端怎麼顯示個long型別都搞不定!
複製程式碼

問題原因

js是弱型別語言,所有的數字型別統稱為Number型別,不區分int、long、double等。而Number是根據IEEE 754標準中的double來實現的,即所有的Number型別都是64位雙精度實型。segmentfault上提供了一個對 IEEE 574標準 非常友好的講解,這裡不再講述。

js內建有32位整數,而number型別的安全整數是53位。如果超過53位的,你不能用json傳遞,需要用其他資料型別,比如字串,或拆分成兩個資料欄位。

解決思路

GitHub開源專案—— Jison,號稱“bison in javascript”,通過它可以實現對後端返回資料的重新定義一個自己的json parser。

在拿到介面請求返回的資料的時候,不用json自帶的那個parse方法,而是通過自己定義了一個json轉換方法,然後再response給前端,這樣一來前端拿到的資料就是一個處理過的json資料。

自定義 json parser

首先使用Node安裝Jison

npm install jison -g
複製程式碼

同時在 lib 目錄下提供了 cli.js 來生成我們想自定義的parser。引數有兩個(詳見cli.js程式碼), cli.js grammaFile lexFile ,grammaFile是語法檔案,lexFile是詞表檔案。

下面就是怎麼寫這兩個檔案了,可以通過 文件 來學習一下這類檔案的語法。如果不想這麼麻煩,還可以在jison作者的另一個專案—— jsonlint 裡面找到,在github中該專案的src目錄下提供了jsonlint.y(grammaFile)和jsonlint.l(lexFile)兩個檔案。使用這兩個檔案可以直接生成jsonlint.js,放到網頁中當json parser來使用。

這裡我們以修改jsonlint裡面的兩個檔案為例生成我們所需要的json parse

我們的目的是讓一些會丟失精度的整數被保留下來,最好的方法是:當整數超過了安全範圍的時候,使用字串表示。我們可以通過修改jsonlint.y來達到這個目的。

原本對JSONNumber的定義是

JSONNumber
    : NUMBER
        {$$ = Number(yytext);}
    ;
複製程式碼

在這裡yytext是要進行解析的原始資料,$$是結果。我們可以修改成

JSONNumber
    : NUMBER
        {$$ = yytext == String(Number(yytext))? Number(yytext): yytext;}
    ;
複製程式碼

==> 生成我們要的 jsonlint.js:

git clone git://github.com/zaach/jsonlint.git
cd src
jison jsonlint.y jsonlint.l
複製程式碼

引入至專案

這裡以Vue專案為例:

1、將自定義的 jsonlint.js 放到 static 目錄下

static.jpg

2、在 index.html 中引入

<script src="./static/jsonlint/jsonlint.js"></script>
複製程式碼

3、在我們請求的返回資料中,做一層攔截轉換

此處以 axios 的實現方法為例:

// transformResponse 選項允許我們在資料傳送到 `then/catch` 方法之前對資料進行改動
axios.defaults.transformResponse = [
  function(data) {
    return jsonlint.parse(data)
  }
]
複製程式碼

總結

綜上,通過自定義JSON轉化避免long型別資料溢位,可以實現long型別資料在前端正常顯示。

注意: 這個方法的確可以實現前端拿到的資料不出現精度丟失問題,但是再瀏覽器中的Preview上檢視資料還是一個丟失的錯誤資料,這個是因為瀏覽器它用的還是自己原始的那個json parse方法。

後記: 小夥伴們,如果有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。如果覺得本文還不錯,記得點個贊哦! 本文首發地址為: Vae's Blog

相關文章