本文へスキップ
jsonbeautifiers
日本語

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の一部の設定はドキュメントごと拒否し、いくつかのパーサーは最初を残します。決して当てにしないでください。このページは、すべての重複を両方の位置とともに報告します。