JSON Stringify / Unstringify
JSONオブジェクトをコードに埋め込むための1行のクォート付き文字列にエスケープ、またはエスケープされたJSON文字列を読みやすいJSONに戻します。
1,007回閲覧
仕組み
Stringifyは実際のJSONを受け取り、1行の、引用符がエスケープされた文字列リテラルに変換します — これは、JSONを別のJSONフィールド、設定値、またはソースコード内の文字列定数に埋め込む際に必要となる形式です。これは内部的には、JavaScriptのJSON.stringify()がまさに行っていることと同じです。値をたどり、文字列を引用符で囲み、制御文字や埋め込まれた引用符をエスケープし、オブジェクトや配列をテキストにシリアライズします。Unstringifyはその逆を行います。エスケープされたJSON文字列(\"や\nを含む)を貼り付けると、JSON.parse()を呼び出してその結果を整形出力するのと同等の、きれいで読みやすい、インデント付きのJSONが得られます。
具体例: {"name":"Ada","tags":["a","b"]}というオブジェクトは、リテラル"{\"name\":\"Ada\",\"tags\":[\"a\",\"b\"]}"に変換されます — 内側の二重引用符ひとつひとつの前にバックスラッシュが付き、全体が外側のひと組の引用符の中に収まるようになります。これは、APIがJSONペイロードを別のJSONレスポンス(webhookの本文、ログの1行、テキスト型のデータベース列など)の中に文字列として包む場合や、JSON値を周囲の構文を壊さずにYAMLファイル、環境変数、ソースコード内の文字列定数に組み込む必要がある場合にまさに起きることです。Unstringifyはその逆を行います — そのエスケープされた塊を貼り付ければ、元の、インデント付きで読みやすい形に戻ります。
この正確なツールを使わない場合でも知っておく価値のあるJSON.stringify()のいくつかの挙動があります。オブジェクトのプロパティにあるundefined値や関数は、静かに取り除かれます(値がundefinedのキーは単純に消えます)が、同じ値が配列の中にある場合は、取り除かれるのではなくnullに変換されます。配列は長さとインデックス位置を保つ必要があるためです。Dateオブジェクトはオブジェクトとしてシリアライズされるのではなく、toJSON()メソッドを介して"2024-01-15T10:30:00.000Z"のようなISO 8601形式の文字列に変換されます。そして、循環参照を含む値 — 直接的または一連のプロパティを通じて自分自身を参照するオブジェクト — はまったくstringifyできません。実際のJSON.stringify()はTypeError: Converting circular structure to JSONを投げます。無限にネストした構造を表現できる有限の文字列は存在しないからです。
知っておくべきこと
Stringifyは、ある特定の意味で非可逆な操作です。静かに取り除かれるもの(関数、undefinedプロパティ、Symbolキー)は、後でunstringifyしても復元できません — 往復変換は、そもそも有効なJSONが表現できるもの、つまり文字列、数値、真偽値、null、単純なオブジェクトと配列だけを保持します。実際のコードでは、JSON.stringify()は値そのものに加えて2つの追加引数も受け取ります。replacer(どのプロパティを含めるか、またはシリアライズ前に値をどう変換するかを正確に制御する関数またはキー名の配列)と、spaceパラメータ(コンパクトな1行の出力を、インデント付きの人間が読みやすいJSONに変える数値または文字列)です — これはまさに、圧縮されたAPIレスポンスとデバッガーが表示する整形済みのバージョンとの違いです。
二重にエンコードされたJSONは、よくある混乱の原因です。ある値がJSON文字列のように見えるのに、その最初の文字が引用符で、内部にバックスラッシュでエスケープされた引用符がたくさん含まれている場合、それはおそらく2回stringifyされています — 一度はアプリケーションによって、もう一度はペイロードがすでにJSON文字列であることに気づかなかった層によってです。一度unstringifyに通せば外側の層が解決します。結果がまだエスケープされているように見える場合は、もう一度実行してください。
よくある質問
コード内のJSON.stringifyとの違いは何ですか?
同じ処理を対話的なツールとして提供しているだけです。実際のJSONを貼り付ければ、通常はJSON.stringify(obj)を書いて得るエスケープされた文字列形式が手に入ります — コンソールがすぐに使えないときに便利です。
なぜJSONをunstringifyする必要があるのですか?
APIやログでは、JSON文字列が別のJSONフィールドの中にネストされていることがあります(二重エンコードされたJSON)— unstringifyはその内側のエスケープされた文字列を、読みやすく解析可能なJSONに戻します。
stringifyした後にプロパティが消えてしまうのはなぜですか?
JSON.stringify()は、値がundefinedまたは関数であるオブジェクトのプロパティを静かに省略します — どちらもJSONでの表現方法が存在しないため、キーはnullとしてシリアライズされるのではなく、完全に削除されます。同じundefined値が配列の中にある場合は、消えるのではなくnullになります。配列は長さとインデックスの順序を保持しなければならないためです。
「circular structure」(循環構造)というエラーが出るのはなぜですか?
あるオブジェクトが、直接、または他のオブジェクトの連鎖を通じて自分自身を参照している場合、JSON.stringify()はそれに対して有限の文字列を生成できず、TypeErrorを投げます。これは、親への逆参照を保持するオブジェクトをシリアライズする際によく起こります(自分のツリーを指し返す子ノード、DOM要素、"self"フィールドを持つクラスインスタンスなど)— 解決策は、シリアライズする前に循環参照を持つプロパティを取り除くか置き換えることで、多くの場合replacer関数を使います。
データはアップロードされますか?
いいえ — どちらの方向の変換もブラウザ内でローカルに実行されます。
関連ツール
問題を報告
JSON Stringify / Unstringify
コメント
まだコメントはありません — 最初のコメントを書いてみましょう!