JSONパーサー
ペイロードを解析し、型・深さ・キー数・全パスを確認できます。
貼り付けたものがブラウザの外に出ることはありません。 connect-src の許可リストにより、これは約束ではなくブラウザによる保証になっています。 自分で確かめる
パースはテキストを構造に変えます。このページでは、その結果として何が出てきたのかを表示します。型、ネストの深さ、キーと配列の数、JavaScriptのパーサーでは壊れてしまう数値、そして重複キーの位置です。
ペイロードを渡されて、まず「これには実際何が入っているのか」を知りたいときに開くページです。
統計から分かること
どれも、ドキュメントに対してコードを書き始める直前に浮かぶ疑問に答えるものです。
- 最大の深さ
- ネストがどこまで続いているか。数百階層を超えるなら、受け取る側の挙動を確認してください。ここでの実測では、V8は100万階層をパースできる一方、およそ4,800階層より深いものはシリアライズを拒否します。CPythonのCスキャナーは、Python側の再帰上限が1,000であるにもかかわらず約17,000階層まで扱えます。
- キー数とオブジェクト数
- マッパーやORMがこなす作業量、そしてメモリ上のツリーの大きさを大まかに見積もる指標です。
- 安全でない数値
- 2^53-1を超える整数がいくつあるか。これがゼロでなければ、JavaScriptの受け取り側はすでにファイルとは違う値を読んでいます。
- 重複キー
- 二度定義されたオブジェクトキーの数と、それぞれの位置。
パースと「形の検証」は別物
ドキュメントが完璧にパースできても、あなたにとって役に立たないことはあります。フィールド名が変わっている、数値が文字列で送られている、オブジェクトのはずが配列になっている。パースが確かめるのは文法であって、契約ではありません。
契約を確かめるには、信頼できるペイロードからJSON Schemaを生成し、以後のペイロードをそれに対して検証してください。
このパーサーがJSON.parseと違うところ
再帰ではなく反復で動くため、ネストの深さはメモリだけが上限です。すべての数値について元のソーステキストを保持するので、黙って丸められることがありません。すべてのトークンの位置を追跡するので、エラーには行と列が付きます。そしてエラーで停止せず復帰するので、壊れたドキュメントでも1回ですべての問題を報告します。
その代償は速度です。ここではおよそ毎秒12〜16MBで、ネイティブのJSON.parseの60〜70MBには及びません。それが情報の対価であり、この処理をバックグラウンドのワーカーで走らせている理由でもあります。
How to do this in code
コードでのパースと、たいてい手遅れになってから知るオプションについて。
js JavaScript
reviverでは元の桁を取り戻せません。reviverが動く時点で、数値はすでに浮動小数点数に変換されているからです。
const value = JSON.parse(text);
// The reviver runs on every key and value, which is how you
// intercept large integers before precision is lost.
const value2 = JSON.parse(text, function (key, val) {
if (typeof val === 'number' && !Number.isSafeInteger(val)) {
// this[key] is the raw value; the original TEXT is not
// available here, which is the limitation.
console.warn('unsafe integer at', key);
}
return val;
}); py Python
Pythonで重複キーを検出する移植性のある方法は、object_pairs_hookだけです。
import json
from decimal import Decimal
# parse_float and parse_int receive the raw TEXT of the number,
# so unlike JavaScript you can keep full precision.
data = json.loads(text, parse_float=Decimal, parse_int=int)
# Detect duplicate keys instead of silently keeping the last
def no_dupes(pairs):
seen = {}
for k, v in pairs:
if k in seen:
raise ValueError(f'duplicate key: {k}')
seen[k] = v
return seen
data = json.loads(text, object_pairs_hook=no_dupes) go Go
// UseNumber keeps the original text instead of converting
// to float64, so large integers survive.
dec := json.NewDecoder(bytes.NewReader(data))
dec.UseNumber()
var v any
if err := dec.Decode(&v); err != nil {
return err
} java Java
ObjectMapper mapper = new ObjectMapper();
// Fail loudly on duplicates rather than keeping the last one
mapper.enable(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY);
// Keep big integers exact
mapper.enable(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS); よくある質問
- パースしたら数値が変わってしまいました。なぜですか?
- JSONの数値には大きさの制限がなく、IEEE 754のdoubleにはあるからです。2^53-1を超える整数や多くの小数は正確に表現できません。Python、Go、Javaはいずれも元のテキストを保持する手段を用意していますが、JavaScriptにはありません。大きなIDを文字列で送るべき理由はここにあります。
- 重複キーはどうなりますか?
- RFC 8259では未定義です。JavaScriptとPythonは最後の出現を残し、GoやJavaの一部の設定はドキュメントごと拒否し、いくつかのパーサーは最初を残します。決して当てにしないでください。このページは、すべての重複を両方の位置とともに報告します。