だるろぐ

だるいぶろぐです

perlとJSONとORM

perlのデータをJSONにしてjavascriptに食わせる。

#!/usr/bin/perl
use strict;
use warnings;
sub say {print @_, "\n"}
use Data::Dumper;sub p {warn Dumper @_;my @c = caller;print STDERR "  at $c[1]:$c[2]\n\n"}
use CGI;
use JSON::XS qw/encode_json/;

main: {
    my %h = (perl_int => 0, perl_char => "0");
    my $j = encode_json(\%h);

    my $q = CGI->new;
    print $q->header;

#print Dumper $j;

print qq{
<html>
<head>
</head>
<body>

    <script type="text/javascript">
    //<![CDATA[


    (function (d) {
        if (d.perl_int) {
            document.write('perl_int:is');
        }
        else {
            document.write('perl_int:not');
        }

    document.write('<br />');

        if (d.perl_char) {
            document.write('perl_char:is');
        }
        else {
            document.write('perl_char:not');
        }
    })($j);

    //]]>
    </script>

</body>
</html>
};
}


結果は

perl_int:not
perl_char:is


型の無いperlでも、JSON::XSでjson形式にするとちゃんとintとcharを判別してくれる。Dumperするとこうなる。

$VAR1 = '{"perl_char":"0","perl_int":0}';

javascriptにおいてはintの0はfalseだが、charの0はtrueである。


ここまではいい。


問題はDBの値をORMで取ってきてそいつをJSON::XSに食わせる場合。
まず、ORMが作ったオブジェクトなんて食わせてもしょうがないので、各ORMに用意されている、オブジェクトの値をhashrefにするメソッドを使う。

Data::ObjectDriverの場合

my $d = Table->lookup(1);
$d = $d->column_values;
p $d;

Data::Modelの場合

my $d = $model->lookup(table => 1);
$d = $d->get_columns;
p $d;

DBIx::Classの場合

# DBIx::Class::ResultClass::HashRefInflatorを使って頑張って
p $d;

これらは全部

{
  'id' => '1'
}

のように、ハッシュのkeyもvalueもクォートして返すので、それをJSON::XSに食わせると当然char型になって返って来る。
なのでjs側で

if (value) {
}

で済むところを

if (value != 0) {
}

と書かねばならないのがめんどくせえというそれだけの話。
文字列比較してるはずなのに何でクォート要らずでいいのかとか考えない。
多分キャストしてくれてるんだろうなと思うがめんどいし暑いので略。