はじめに
でかい数字をJSONで表現するとどうなるんだろう
// number
> JSON.stringify({ "a": 911232123123127987297})
'{"a":911232123123128000000}'
> JSON.parse(`{ "a": 911232123123127987297}`)
{ a: 911232123123128000000 }
// BigInt
> JSON.stringify({ "a": 911232123123127987297n})
Uncaught TypeError: Do not know how to serialize a BigInt
at JSON.stringify (<anonymous>)
at <anonymous>:2:6
> JSON.parse(`{ "a": 911232123123127987297n}`)
Uncaught SyntaxError: Expected ',' or '}' after property value in JSON at position 28
at JSON.parse (<anonymous>)
at <anonymous>:2:6
Do not know how to serialize a BigInt
なんかBigIntはうまく扱えないようです。なんででしょ
BigIntについて
MDNみるのが一番良い。
JSON.stringify() を BigInt 値に対して使用すると TypeError が発生します。 BigInt 値は既定で JSON のシリアライズに対応していないためです。ただし、 replacer 引数を JSON.stringify で使用すると、 BigInt のプロパティをエラーなしでシリアライズすることができます。
長整数になる値が含まれると思われる JSON データがある場合は、 reviver 引数を JSON.parse で使用することで取り扱うことができます。
なんで自分でパースとシリアライズ定義時にいちいち変換かけないかんの?
JSONでのBigIntサポートは議論されたが互換性が優先された
要点絞ると
- JSON仕様はRFC 8259とECMA-404 2ndでFIXしており、BigIntについての取り扱い方が定義されてない
- パーサーやシリアライザの後方互換性を考えるとChrome/V8や一部の実行環境だけパース可能にしてもデータのポータビリティが失われてしまう
- そもそも JSON文字列中の数値がnumberなのかbigintなのかを判別する仕様がJSON自体に存在してない
- 日付型とかも文字で表現して回避してるし、前述のようにreviver/replacerで吸収するのが混乱が一番少ない
- 新しいJSON拡張フォーマットとその処理系に期待しても良い。けど、議論時点では互換性とJSONとしての仕様準拠を重視した
という大激論の末、BigIntはJSONでうまく扱われない子になってしまったようです
JSONでBigIntを扱えるようにしようとする活動もろもろ
JSOX
J-SOXと似ててググらビリティが悪い。jsonxっていう全然用途が違うものもあるので注意
JSON5/JSON6 といった拡張JSONフォーマットを提案している方がBigInt対応も考慮した拡張JSONフォーマットとして定義
プロポーザル的な立ち位置でJSON拡張フォーマットとしてBigIntをうまく扱えるようにしようとしている
import { JSOX } from "npm:jsox"
const bigintObj = { a: 8982979873927987392837212139794n }
console.log(JSOX.stringify(bigintObj))
// {a:8982979873927987392837212139794n}
console.dir(JSOX.parse(JSOX.stringify(bigintObj)))
// { a: 8982979873927987392837212139794n }
jsoxは n
表記でシリアライズされるしパースするとちゃんとBigIntとして扱う
json-bigint
JSONbig.parse
, JSONbig.stringify
といったネームスペースが増える
ただし2年くらい更新止まってる
json-bigint-native
前述の json-bigint
をフォークして作っていて比較的手も入ってる
import JSONbig from "npm:json-bigint-native"
const bigintObj = { a: 8982979873927987392837212139794n }
console.log(JSONbig.stringify(bigintObj))
// {"a":8982979873927987392837212139794}
console.dir(JSON.parse(JSONbig.stringify(bigintObj)))
// { a: 8.982979873927987e+30 }
シリアライズした結果は 123n
じゃなくてそのままでかいDigitが入る
パースすると指数表記なんでnumberになっているっぽい
余談 - JSONの拡張フォーマット色々
JSONC
コメントが書けるJSON
VSCodeの設定とかはこれ
JSON-Scheme
JSONに型情報を持ち込むやつ
CBOR/BSON
JSONをバイナリで表現しようぜのやつ
CBOR
BSON