WebService::Simple::Parser::JSONがUTF-8エンコードされたJSONで文字化けてハマった

簡単な例として

{"name":"ほげ"}

UTF-8エンコードされている日本語を含むJSONを返すAPIがあったとして、WebService::Simpleからそれを取得して値を表示しようとしたとき。

#!/usr/bin/env perl
use strict;
use warnings;

use Amon2::Lite;
use WebService::Simple;

get '/' => sub {
    my ($c) = @_;
    my $ws = WebService::Simple->new(
        base_url => 'http://****.****/****.json',
        response_parser => 'JSON',
    );
    my $json = $ws->get->parse_response;
    return $c->render('index.tt', {
        name => $json->{name},
    });
};

__PACKAGE__->to_app();

__DATA__
@@ index.tt
<!doctype html>
<html>
<body>Hello, [% name %]</body>
</html>

こうなる。

どうすればコレを避けられるのかが分からなくて迷走した。


どんぞこ日誌(2010-04-06) によると、

もし外部(ネットやファイルなど)からきたJSONデータがUTF-8エンコードされてるのがわかっているなら、decode_jsonかutf8オプションを有効にしたJSONオブジェクトを使うべきです。

WebService::Simple::Parser::JSONでは

sub new
{
    my $class = shift;
    my %args  = @_;

    my $json  = delete $args{json} || JSON->new;
    my $self  = $class->SUPER::new(%args);
    $self->{json} = $json;
    return $self;
}

となっていて、普通に使うとJSONのutf8オプションは有効になっていない。ので、呼ぶ側でuse JSON;して、

    my $ws = WebService::Simple->new(
        base_url => 'http://****.****/****.json',
        response_parser => {
            module => 'JSON',
            args => {
                json => JSON->new->utf8,
            }
        }
    );
    my $json = $ws->get->parse_response;

のようにutf8オプションが有効になっているJSONオブジェクトを明示的に渡すとか、

    my $ws = WebService::Simple->new(
        base_url => 'http://****.****/****.json',
        response_parser => 'JSON',
    );
    my $json = decode_json($ws->get->content);

のようにdecodeを任せずに自前でやってしまう、とかで対処する

…のかな?