JavaScript
JSON
npm

JSON.stringifyの追加引数とソート

昨日行き詰まっていたところでどうしようか考えて、結局はjkr2255/sorted-json-stringifyなんてものを作ってしまいました。

オブジェクトの管理に依存せず並べ替える

突然ですが、JSON.stringify({'30': 'a', '5': 'b'})はどうなると思いますか?多くのブラウザでは、'{"5":"b","30":"a"}'という結果となります。実際、他の値を入れてみた感触でも、数値キーに関しては別枠でハンドリングしているような、妙な挙動を示しました。

実際にこのようなブラックボックスに遭遇してしまった以上、デフォルトのJSON.stringifyでは、順番がどうなるかなんてわかりません。とはいえ、JSON.stringify全部を再実装するのも面倒だし、出来上がったJSONのパースも色々考えると複雑そうだし…みたいなことを考えていました。

JSON.stringifyの仕様を見ていて

そんな中でMDNを見ていると、JSON.stringifyには3つの引数があると書いてありました。

  • replacer…ルールに従ってオブジェクトを置き換える
  • space…見栄えをよくするために、JSONにスペースを挿入する

つまり、spaceでインデントをかけた上で、そのインデントを数える、という手法を取れば、改めて大掛かりにパースしなくてもJSONの入れ子構造を抽出できるということがわかりました。

実際に作ってみた

ということで、

  1. space = 1で、デフォルトのJSON.stringifyを使って、改行&インデントありのJSONを生成する
  2. 行ごとに区切って、インデントのスペースの数を数えておく
  3. インデント位置で構造を把握しつつ、JSONを並べ替えていく
  4. 最後にインデントなどを調整して、最終型のJSONを作る

このような流れで実装することができました。なお、ハマった箇所としては、space=0のときは"foo":"bar"のようにキーと値が密着するのに対して、spaceが入る場合には"foo": "bar"とスペースが1つ入る、となっていて、そこの差で一度コケました。

なお、今回これを作るのにあたって、Mocha、Chai、Rollupと言ったライブラリをはじめて使ってみました。