JSON 圧縮

JSON を最小の正当な形に圧縮したり、2 スペース・4 スペース・タブで整形したりできます。キーのソートに対応し、削減バイト数をリアルタイム表示。処理はすべてブラウザ内で完結します。

データを変えないバイトだけを削ぎ落とす。 JSON を貼り付けると省略可能な空白をすべて取り除きます。整形モードに切り替えれば、好みのインデントで組み直せます。キーのソートは 2 つの文書を比較可能にし、バイト数表示は圧縮で実際に何が得られたかを示します。

圧縮がすること・しないこと

取り除かれるのは空白だけ

JSON の文法では、任意のトークンとトークンの間にスペース・タブ・改行を置けますが、そのいずれも意味を持ちません。圧縮とは、この省略可能な文字をすべて取り除く操作です。{"a": 1, "b": 2}{"a":1,"b":2} になります。パース後の値はメモリ上で完全に同一なので、送信前にこの処理を挟むのは常に安全です。

一方、圧縮は「データを圧縮」しません。キー名も文字列の中身も数値の精度もそのまま残ります。1 万件のレコードで同じ 20 個のフィールド名を繰り返しているせいで文書が大きいのなら、圧縮はインデントを削るだけで繰り返し自体は残ります。それは gzip か、別のスキーマ設計が解決する問題です。

削減が本当に効く場面

整形済みの典型的な API レスポンスでは、空白が生サイズの 10〜30% を占めます。有望に聞こえますが、ほとんどの HTTP レスポンスは転送時に gzip や brotli で圧縮済みであり、これらのアルゴリズムは連続する空白をきわめて上手に扱います。gzip 後では、圧縮版と整形版の差が 2% を下回ることも珍しくありません。

それでも圧縮が価値を持つ場面は 3 つあります。HTML ページや JavaScript バンドルに直接埋め込むデータ(個別には圧縮されないため)、データベースのカラムやキャッシュへの保存(占有するのは生の文字列そのもの)、そして URL パラメータや長さ上限のあるメッセージ欄など、文字数の上限と戦う場面です。

比較のためにキーをソートする

JSON のオブジェクトは仕様上は順序を持ちませんが、シリアライザは必ず何らかの具体的な順序でキーを書き出し、2 つのシステムが同じ順序を選ぶことはまずありません。その結果、論理的には同一の文書どうしのテキスト差分が読めたものではなくなります。

出力前にキーを再帰的にソートすれば、両方の文書が同じ正規形になり、行単位の差分がようやく本当の変更だけを示すようになります。再現可能ビルドやコンテンツアドレス方式のキャッシュも同じ手法に支えられています。ただしソートは出力バイト列を変えるため、ソート後の文書は元のシステムが出力したものと同一ではありません。比較には使えますが、署名検証を伴う往復には使わないでください。

オープンソースに関する注記:バニラ JavaScript で実装。サードパーティライブラリは使用していません。

よくある質問

データはどこかにアップロードされますか?
いいえ。解析と再シリアライズはブラウザ組み込みの JSON 関数を使ってすべてローカルで行われるため、文書がページの外に出ることはありません。
圧縮するとデータが変わりますか?
変わりません。取り除かれるのはトークン間の意味を持たない空白だけです。キー・文字列・数値・真偽値はそのまま残り、パース結果は同一です。
削減量が思ったより少ないのはなぜですか?
カウンターは生の UTF-8 バイト数を測っています。元の JSON がすでに簡潔だったり、サイズの大半がインデントではなく文字列の中身だったりすると、削れる空白は多くありません。
サーバーで gzip を使っていても圧縮すべきですか?
ネットワーク転送では通常不要です。gzip は連続する空白を非常に効率よく圧縮するため、追加の効果は 2% を下回ることが多いです。埋め込みや保存用の JSON にはなお有効です。
キーのソートは何をしますか?
各オブジェクトのキーを再帰的にアルファベット順へ並べ替えます。論理的に同一な 2 つの文書が同じテキストになるため、行単位の差分が実用的になります。
正しそうな JSON で解析エラーが出るのはなぜですか?
厳密な文法は末尾のカンマ、シングルクォートの文字列、コメント、引用符なしのキーを認めません。これらは JavaScript のオブジェクトリテラルの機能であり JSON ではありません。