HOME備忘帳

PerlのCGIでJSONデータを作る

Javascriptからの問合せなどで、CGIプログラムからJSONデータを出力したいときの小技です。
ちょっとしたデータなら、Perlの標準モジュールであるData::Dumperで作ってしまいます。

(2014/08/21追記) 出力結果の日本語部分を読みやすくする対応について追記しました。サンプルコードもやや変更しました。

(2015/07/29追記) モジュールの変数をおもむろに上書きするのではない書き方も追加。…というか、そんなに気を使って書くならJSONモジュール使った方がいい。

サンプルコード

#!/usr/bin/perl
use strict;
use utf8;
use Data::Dumper;

# JSONで返したいデータ
my $json = {
	number => 1,
	list => [10, 20, 30],
	text => '全角文字もそのままいきます',
};

# Data::Dumperのオプションを設定して
$Data::Dumper::Indent = 0;	# インデントなし
$Data::Dumper::Useqq = 1;	# ハッシュのキーはダブルクォートでくくる
$Data::Dumper::Terse = 1;	# evalするための、最初の「$VAR =」は不要
$Data::Dumper::Pair = ":";	# ハッシュの区切り文字

# ダンプ。
my $output = Dumper($json);

# 出来上がったデータを返す
binmode STDOUT, ":encoding(utf8)";
print "Content-type: applecation/json; charset=utf-8\n\n",
		$output;

exit;

実行結果は以下のような感じです。

Content-type: applecation/json; charset=utf-8

{"number":1,"text":"\x{5168}...(省略)...\x{3059}","list":[10,20,30]}

値1個だけ、とかなら手でJSON形式のテキストを作ってもいいのですが、多少でも面倒なものは、Dumperに頑張ってもらうのが便利でした。うっかり間違えないので。

また、出力だけでなく入力もJSONとなると、ちゃんとJSONモジュールを使うほうが良い気がします。

私は、jQueryでフォーム送信イベントを奪って、何かの処理した後に.seriarize()したデータをcgiに.post()、コールバックはJSONで受け取りたい…みたいな時に使いました。

(2015/04/03追記) 普通の用途で(デバッグのために)Dumper使ってる場合も、その出力がJSON的になるのでNGですね。デバッグ受け取りもJSONが良いとき意外は。

(2015/07/29追記) 書くほどのことでもないのですが、念のため。Dumper内変数を汚染しないためにはこんな感じ。

# 出力したいデータでData::Dumperオブジェクトを作って
my $d = Data::Dumper->new($json);

# 出力オプション設定。
$d->Indent(0);
$d->Useqq(1);
$d->Terse(1);
$d->Pair(":");

# ダンプ。
print $d->Dump;

[ ページ先頭へ ]

日本語などを読めるように

上記の結果を見ていただいたとおり、日本語などが「\x{0000}」の形式になって可読性は悪いです。
この変換は、Data::Dumper内で文字列をクオートでくくるサブルーチン、qquoteで行われています。

Data::Dumperのソースコードを見た限り、何かのオプションでこの変換をoffにする…とかは無さそうで、文字数<バイト数なら無条件に変換している模様です。

それが嫌な場合、たとえば以下のような感じでサブルーチンを上書きすれば、人間が読める状態で出力できます。

#!/usr/bin/perl
use strict;
use utf8;
use Data::Dumper;

# JSONで返したいデータ
my $json = {
	number => 1,
	list => [10, 20, 30],
	text => '全角文字や
改行、	タブ、\@記号も。',
};

# Data::Dumperのオプションを設定して
$Data::Dumper::Indent = 0;	# インデントなし
$Data::Dumper::Useqq = 1;	# ハッシュのキーはダブルクォートでくくる
$Data::Dumper::Terse = 1;	# evalするための、最初の「$VAR =」は不要
$Data::Dumper::Pair = ":";	# ハッシュの区切り文字

# qquoteを上書き
{
	no warnings 'redefine';
	sub Data::Dumper::qquote {
		local($_) = shift;
		s/([\\\"\@\$])/\\$1/g;	# 記号関係をエスケープ。

		# タブや改行などをメタ文字に置き換え
		my %esc = (
			"\a" => '\a',
			"\b" => '\b',
			"\t" => '\t',
			"\n" => '\n',
			"\f" => '\f',
			"\r" => '\r',
			"\e" => '\e',
		);
		s/([\a\b\t\n\f\r\e])/$esc{$1}/g;

		return qq|"$_"|;	# 結果をダブルクオートでくくって返す
	}
}

# データのダンプを出力
binmode STDOUT, ':encoding(utf8)';
print "Content-type: applecation/json; charset=utf-8\n\n",
		Dumper($json);

exit;

下記が出力結果。

Content-type: applecation/json; charset=utf-8

{"number":1,"text":"全角文字や\n改行、\tタブ、\\\@記号も。","list":[10,20,30]}

そもそも間に合わせ目的なので、これ以上手間はかけないでいいかなと…。

[ ページ先頭へ ]

参考

cpanのData::Dumper

最終更新日:2015/07/29

[ ページ先頭へ ]