■2010-04-06
* [Perl][メモ] JSONのドキュメントに追加した部分
tokuhiromさんに(en|de)code_json
と(to|from)_json
どっちを使ったほうが良いのか初心者にわかるようにしたらと言われたので、最近のJSON.pmのドキュメントには(en|de)code_json
がオススメである旨が付け足されています。さらに念を押して、一応自分ではわかりやすく説明したつもりの"HOW DO I DECODE A DATA FROM OUTER AND ENCODE TO OUTER"というセクションが加わっています。
で、この日誌にperl json 文字化けとかで検索してくる人がたまにいて、たぶんこのページがヒットするのでしょうが、(自分で訳しておいて何ですが)これ見てもわかりにくいんじゃないかなあという気がします。また、先のgoogleの検索結果のトップには現状Kawa.netさんの2008年初頭のページが最初の方に表示されますが、UTF8フラグ云々の話が書いてあって初心者向けじゃないと思うので、何かの参考になるかもしれないので"HOW DO I DECODE A DATA FROM OUTER AND ENCODE TO OUTER"の和訳を掲載しておきます。
※PerlとUNICODEについてはこの辺りでperluniから始まるファイルを読んでみてください。
どのように外部からのデータをdecodeするか、外部へとencodeするか
このセクションはPerl5.8以降を想定しています。
もし外部(ネットやファイルなど)からきたJSONデータがUTF-8でエンコードされてるのがわかっているなら、decode_json
かutf8オプションを有効にしたJSONオブジェクトを使うべきです。decodeされた結果はUNICODEキャラクタを含んでいるでしょう。
# ネットから my $json = JSON->new->utf8; my $json_text = CGI->new->param( 'json_data' ); my $perl_scalar = $json->decode( $json_text ); # ファイルから local $/; open( my $fh, '<', 'json.data' ); $json_text = <$fh>; $perl_scalar = decode_json( $json_text );
もし外部データがUTF-8でないなら、まずはそのデータをdecode
します。
use Encode; local $/; open( my $fh, '<', 'json.data' ); my $encoding = 'cp932'; my $unicode_json_text = decode( $encoding, <$fh> ); # あるいは下のように # # open( my $fh, "<:encoding($encoding)", 'json.data' ); # $unicode_json_text = <$fh>;
この場合、$unicode_json_text
はもちろんUNICODEな文字列です。ですから、decode_json
やutf8オプションを有効にしたJSONオブジェクトは使えません。代わりに、utf8オプションを無効にしたJSONオブジェクトか、from_json
を使います。
$perl_scalar = $json->utf8(0)->decode( $unicode_json_text ); # または $perl_scalar = from_json( $unicode_json_text );
あるいはencode 'utf8'
してからdecode_json
します。
$perl_scalar = decode_json( encode( 'utf8', $unicode_json_text ) ); # 効率的ではない
今度は、$perl_scalar
をJSONデータに変換して外部(ネットやファイルなど)へと送ります。
通常あなたのデータはUNICODE文字列を含んでいます。UTF-8でエンコードされたJSONデータにするなら、encode_json
かutf8オプションを有効にしたJSONオブジェクトを使うべきです。
print encode_json( $perl_scalar ); # または print $json->utf8->encode( $perl_scalar );
何らかの理由で$perl_scalar
がUNICODEではなく$encoding
でエンコードされた文字列を含んでいる場合、そのキャラクタはlatin1
とみなされます(perlはあなたの$encoding
を気にしません)。encode_json
やutf8オプションを有効にしたJSONオブジェクトは使えません。代わりにutf8オプションを無効にしたJSONオブジェクトか、to_json
を使います。この結果生成されるテキストはUNICODEですが、出力しても問題ありません。
# $perl_scalarは$encodingでエンコードされた値を持つ $unicode_json_text = $json->utf8(0)->encode( $perl_scalar ); # または $unicode_json_text = to_json( $perl_scalar ); # $unicode_json_textは0x100未満のキャラクタで構成されている print $unicode_json_text;
あるいは文字列を全部deocde $encoding
してからencode_json
する:
$perl_scalar->{ foo } = decode( $encoding, $perl_scalar->{ foo } ); # 全部の内容にたいして上記操作を…… $json_text = encode_json( $perl_scalar );
この方法は適切ではありますが、たぶん効率悪いです。
Encodeやperluniintroを参照してください。
■2010-04-22
* [Perl][メモ] perl-casual #02のライブコーディングで気づいた点
昨日のperl-casual #02でlestrratさんがtwitter apiからデータを取得するプログラムのライブコーディングをされました。その中で、全然本質的じゃないところなのですが、一点気づいたことがあるので補足を。
LWPを使ってTwitter APIを叩き、JSONデータを展開する部分で:
# $responseはHTTP::Responseのオブジェクト decode_json( $response->decoded_content );
というコード。昨日はちゃんと動作していましたが、場合によっては"Wide character in subroutine entry"でcroakします。これは、$response->decoded_content
が返す値がUnicodeだからです。
ではなぜ昨日は大丈夫だったかというと、Twitter APIが返したJSONデータは文字列が\uでエスケープされていました。つまり、$response->decoded_content
の返値はUnicode文字列ではあるものの実質asciiだけで構成されています。こういう場合はdecode_jsonもcroakしません。ですから、上記の部分は
decode_json( $response->content ); # 生のutf-8データ
とした方が安全です。decoded_contentが使いたい場合は
JSON->new->utf8(0)->decode( $response->decoded_content );
などのようにすると良いでしょう。decode_json
の動作については、私の先日の記事なども。
_ lestrrat [本当のスクリプトは decode_json $response->content なんだけど、そっちなら大丈夫・・・..]
_ makamaka [昨日はお疲れ様でした〜 はい、それで大丈夫です!> decode_json $response->conten..]