[Catalyst] formfu->JSON
Charles Bailey
bailey.charles at gmail.com
Tue Nov 11 02:19:15 GMT 2008
On Mon, Nov 10, 2008 at 3:24 PM, Paul Tichonczuk <paultk at gmail.com> wrote:
> Hi,
> I'm new to Catalyst.
> I've gone through both the Catalyst Tutorial and the Catalyst book.
>
> I'm trying out FormFu + Jemplate.
>
> Basic problem I've come up to is redering a FormFu form into JSON for
> sending to the client.
>
> I keep getting:
> [error] Caught exception in MyApp::View::JSON->process "encountered
> object 'MyApp::Controller::Books=3DHASH(0x9f4406c)', but neither
> allow_blessed nor convert_blessed settings are enabled at
> /usr/local/share/perl/5.8.8/JSON/Any.pm line 426."
It looks as though you're trying to render your controller object into
the JSON response. By default, C::V::JSON tries to serialize all keys
in the stash; have you perhaps stored some object references there?
If your intent is to render only specific values from the hash, you
should look at the expose_stash configuration option to C::V::JSON.
If you do want to serialize object, then you'll need to override the
default encode_json() and use a JSON encoder with the convert_blessed
or allow_blessed options set, perhaps combined with TO_JSON() in the
object class(es). I've attached a (very underdeveloped and
underdocumented) utility class that I've found helpful when dealing
with simple hash-based classes.
-- =
Regards,
Charles Bailey
Lists: bailey _dot_ charles _at_ gmail _dot_ com
Other: bailey _at_ newman _dot_ upenn _dot_ edu
-------------- next part --------------
#!/usr/local/bin/perl
#
use 5.010;
package JSON::Able;
use Moose::Role;
use Carp qw(croak);
use Scalar::Util qw(blessed reftype refaddr);
use JSON qw();
our($VERSION) =3D '0.01';
our($REVISION) =3D '$Revision$' =3D~ /: (\d+)/ ? $1 : 0;
# Used only for non-Moose consumer classes
our(@EXPORT_OK) =3D qw(TO_JSON encode_json as_json
json_key_filter json_value_transform json_pretty);
our(%EXPORT_TAGS) =3D (all =3D> [ @EXPORT_OK ]);
sub import { require Exporter; goto &Exporter::import; }
# Track configuration options for target packages, since JSON::XS does
# not provide for any way to pass options to TO_JSON. We could make
# this a wrapper around our own method which does accept options, but
# that would compete with Moose::Role's injection of methods . . .
my(%Config);
sub _get_config_data {
my($thing,$create) =3D @_;
my $pkg =3D blessed($thing) // $thing;
if (ref $thing) {
my $oid =3D (blessed($thing) || reftype($thing)) . '(' . refaddr($thing=
) . ')';
return $Config{$oid} if exists $Config{$oid};
return $Config{$pkg} if exists $Config{$pkg};
$Config{$oid} ||=3D {} if $create;
return $Config{$oid} || {};
}
$Config{$pkg} ||=3D {} if $create;
return $Config{$pkg} || {};
}
sub _configure_option {
my($opt,$thing,$val) =3D @_;
my $cfg =3D _get_config_data($thing, @_ > 2);
my $old =3D $cfg->{$opt};
if (@_ > 2) {
$cfg->{$opt} =3D $val;
}
$old;
}
=
sub json_key_filter { _configure_option('KEY_FILTER', @_); }
sub json_value_transform { _configure_option('VALUE_TRANSFORM', @_); }
sub json_pretty { _configure_option('PRETTY', @_); }
sub TO_JSON {
my $self =3D shift;
my $pkg =3D blessed $self;
my $vers =3D $self->VERSION;
my $cfg =3D _get_config_data($self);
my $key_filter =3D $cfg->{KEY_FILTER} || =
sub { $_[0] =3D~ /^(?!_)/ };
my $val_xform =3D $cfg->{TRANSFORM_VALUES} ||
sub { my($val,$isset) =3D @_; return $isset ? $val : (); };
my(%attrs,%tojson);
# If you implement this interface, we assume you're descended from
# or behave like Moose
if ($self->can('meta') and
$self->meta->can('compute_all_applicable_attributes')) {
%attrs =3D map {$_->name =3D> $_->has_value($self) }
$self->meta->compute_all_applicable_attributes;
}
elsif (reftype($self) eq 'HASH') {
%attrs =3D map { $_ =3D> 1 } keys %$self;
}
else {
croak("Don't know how to get attributes of \"$self\" for JSON");
}
foreach my $k (keys %attrs) {
next unless $key_filter->($k);
my $val =3D $self->can($k) ? $self->$k : $self->{$k};
my(@rslt) =3D $val_xform->($val, $attrs{$k});
next unless @rslt;
$val =3D $rslt[0];
# Special-case DateTime, which is used frequently in the project
# hierarchy. It doesn't provide a TO_JSON method, but has a
# perfectly serviceable overloading of ""
if (blessed($val) and $val->isa('DateTime')) { $val =3D "$val"; }
$tojson{$k} =3D $val;
}
# Include some metadata to simplify recreating objects
# if data ever comes back
$tojson{_source_class} =3D $pkg;
$tojson{_source_class_version} =3D $vers;
\%tojson;
}
sub encode_json {
my $cfg =3D _get_config_data($_[0]);
JSON->new->pretty($cfg->{PRETTY} // 1)->
convert_blessed->encode(shift);
}
*as_json =3D \&encode_json;
1;
__END__
=3Dhead1 NAME
JSON::Able - Role providing JSON serialization
=3Dhead1 SYNOPSIS
# Moose-based class
package My:Project::SomeData;
with 'JSON::Able';
package main;
my $obj =3D My::Project::SomeData->new(\%data);
require JSON;
my $json =3D JSON->new->pretty->convert_blessed->encode($obj);
my $same_json =3D $obj->as_json;
my $still_same_json =3D $obj->encode_json;
# Non-Moose-based class
package Some::Class;
use JSON::Able ':all';
=
package main;
my $obj =3D Some::Class->new(foo =3D> 'bar', baz =3D> 'quux');
require JSON;
my $json =3D JSON->new->pretty->convert_blessed->encode($obj);
my $same_json =3D $obj->as_json;
my $still_same_json =3D $obj->encode_json;
=
=3Dhead1 DESCRIPTION
This package implements a Moose role that provides for JSON
serialization of Moose attributes of the consuming class in a manner
compatible with L<JSON::XS>.
While designed for use with Moose-based classes, it can also be mixed
into any class implementing an object structure based on a hash
reference. In this instance, you need to import at least the
L</TO_JSON> method for it to do anything useful. Note that, as of this
writing, you cannot "JSON enable" a class simply by inheriting from
F<JSON::Able>, since L<JSON::XS> requires a F<TO_JSON>
method I<in the class to be serialized>, not just in one of its base classe=
s.
=3Dhead2 EXPORT
None by default, and none required when used as a Moose role.
If you are using this class directly to support JSON serialization of
a non-Moose-based class, you need to import at least L</TO_JSON>. You
may also import F<as_json> and F<encode_json> if you desire. Note
that L<JSON> exports the F<encode_json> method by default, =
=3Dhead1 BUGS AND CAVEATS
Are there, for certain, but have yet to be cataloged.
=3Dhead1 REVISION
$Revision$ $Date: 2008-03-26T18:38:58.710706Z $
=3Dhead1 AUTHOR
Charles Bailey, E<lt>bailey at newman.upenn.eduE<gt>
=3Dhead1 COPYRIGHT
Copyright (C) 2008 by Charles Bailey
=3Dcut
More information about the Catalyst
mailing list