[小ネタ]Javascriptで大きい桁の整数をJSONパースする時の注意点

桁あふれでハマった話
2020.04.10

JavascriptでJSONにパースしたいデータに大きい桁の整数があった場合は注意が必要です。
16桁を超えた数値を含む場合、超えた桁数分端数が丸め処理されてしまいます。
その場合、json-bigintを使って回避できます。

JSONとして解析する文字列

const jsonStr = '{"id": 6476060033855273896}';

Javascript標準ビルトインのJSON.parseでパースすると以下のようなデータになります。

JSON.parse(jsonStr);
> { id: 6476060033855274000 } // (_□_;)!!

桁はそのままですが数値が変わってしまいました。エラーは出ていません。

json-bigintを使うと、

const JSONbig = require('json-bigint');

const jsonStr = '{"id": 6476060033855273896}';
JSONbig.parse(jsonStr);
> { id: 6476060033855273896 }

このように正しくパースできました。

stringに変換する

ちなみに JSONbig.parse() でパースした場合、データは以下のようなobjectとなっています。

const result = JSONbig.parse(jsonStr);
console.log(typeof(result.id));
> object

> result.id = {
    "s": 1,
    "c": [
        64760,
        60033855273896
    ],
    "e": 18,
    "_isBigNumber": true
   }

これをstringで取得するためには以下のように storeAsString をtrueに指定することでstringに変換してくれます。

const JSONbig = require('json-bigint')({"storeAsString": true});
const result = JSONbig.parse(jsonStr);
> result = { id: '6476060033855273896' }

console.log(typeof(result.id));
> string

後続の処理でstringとして扱う場合は上記のオプションを使いましょう。

解説

そもそもなぜ、丸め処理されてしまうのかと言うと、JavaScriptの中で正確に扱える最大整数値(Number.MAX_SAFE_INTEGER)を超えているからです。

最大整数値を出力してみると、

console.log(Number.MAX_SAFE_INTEGER);
> 9007199254740991

となります。

JSONの number がECMAScriptの Numbers に対応しているため、結果的にIEEE754の定める倍精度浮動小数点数として扱われているのが理由となります。

json-bigintや他のライブラリを使って回避しましょう。

参考

MAX_SAFE_INTEGER
JSON.parse
ECMAscript json.parse