読者です 読者をやめる 読者になる 読者になる

Masteries

技術的なことや仕事に関することを書いていきます.

PerlでJSONを生成するときにJSONのキーの順番をソートする

今日はJSON.pmの小ネタをお送りします. ...といっても割と既知な情報では? という気がしているのですが, 自分は知らなくて10分くらいJSON.pmのドキュメント読んだりしてやっと見つけたので, 備忘録的に残しておきます.

さて, JSON.pmを使って, 普通にPerlのハッシュによるデータ構造をJSONに変換しようとすると, 生成されたJSONのキーの順序は生成する度に異なる順序になってしまいます.

use strict;
use warnings;

use JSON qw/ encode_json /;

my $json = encode_json({ a => 1, b => 2, c => 3 });
print "$json\n";

例えば, このコードをPerl 5.20で実行すると...

$ perl json.pl
{"c":3,"a":1,"b":2}
$ perl json.pl
{"a":1,"c":3,"b":2}
$ perl json.pl
{"b":2,"c":3,"a":1}

こんな感じで, 毎回キーの順番が異なってしまいます(例えば, 1回目はc, a, bの順番ですが, 2回目はa, c, bになっていますよね).

APIを実装していて, ユーザに対してJSONを返す場合であれば, これでも特に問題はありません. しかし, 何かしらのマスターデータからJSON形式のデータを生成してファイルに保存する... という場合, 生成後のJSONのキーの順番が毎回バラバラだと, 例えばJSONが書き込まれたファイルをGitで管理している場合, (JSONのキーの順序がJSONを生成する度に変化するので)毎回不要な差分が生じてしまい, 不便です.

こういう時は, JSON.pmのCanonicalモードを使えばいいそうです.

use strict;
use warnings;

use JSON;

my $json = JSON->new->utf8->canonical->encode({ a => 1, b => 2, c => 3 });
print "$json\n";

こういう感じにしてやると...

$ perl json.pl
{"a":1,"b":2,"c":3}
$ perl json.pl
{"a":1,"b":2,"c":3}
$ perl json.pl
{"a":1,"b":2,"c":3}

何度JSONを生成しても, 毎回同じキーの順序になっていることがわかります!

余談

ちなみに, canonicalだけでなくutf8も通しているのは, canonicalを有効にしながら, encode_jsonと同じようにJSONを生成したいからです.

要するに, JSON.pmを普通に使う場合の,

use strict;
use warnings;

use JSON qw/ encode_json /;

my $json = encode_json({ a => 1, b => 2, c => 3 });
print "$json\n";

というコードは,

use strict;
use warnings;

use JSON;

my $json = JSON->new->utf8->encode({ a => 1, b => 2, c => 3 });
print "$json\n";

と同じ事をしている, というわけです.

以上, PerlのJSON.pmについての小ネタでした.

あわせてよみたい