JSON-Eval
view release on metacpan or search on metacpan
lib/JSON/Eval.pm view on Meta::CPAN
package JSON::Eval;
our $AUTHORITY = 'cpan:TOBYINK';
our $VERSION = '0.002';
use Scalar::Util qw(blessed);
sub new {
my $class = shift;
my $json = @_ ? $_[0] : do { require JSON::MaybeXS; JSON::MaybeXS->new };
bless \$json, $class;
}
sub AUTOLOAD {
my $self = shift;
our $AUTOLOAD;
( my $method = $AUTOLOAD ) =~ s/.*:://;
my $r = $$self->$method(@_);
return $self if $r == $$self;
$r;
}
sub decode {
my $self = shift;
my $o = $$self->decode(@_);
$self->eval_object($o);
}
sub encode {
my $self = shift;
my $o = $self->deparse_object(@_);
$$self->encode($o);
}
sub eval_object {
my $self = shift;
my ($o) = @_;
if (ref $o eq 'HASH' and keys(%$o)==1 and exists $o->{'$eval'}) {
return $safe_eval->($o->{'$eval'});
}
if (ref $o eq 'HASH' and keys(%$o)==1 and exists $o->{'$scalar'}) {
my $x = $self->eval_object($o->{'$scalar'});
return \$x;
}
if (ref $o eq 'ARRAY') {
local $_;
return [ map(ref($_)?$self->eval_object($_):$_, @$o) ];
}
if (ref $o eq 'HASH') {
local $_;
return { map { $_ => ref($o->{$_})?$self->eval_object($o->{$_}):$o->{$_} } keys %$o };
}
$o;
}
sub deparse_object {
my $self = shift;
my ($o) = @_;
if (ref $o eq 'CODE') {
require PadWalker;
my $lexicals = PadWalker::closed_over($o);
if (keys %$lexicals) {
require Carp;
Carp::croak("Cannot serialize coderef that closes over lexical variables to JSON: ".join ",", sort keys %$lexicals);
}
require B::Deparse;
my $dp = 'B::Deparse'->new;
$dp->ambient_pragmas(strict => 'all', warnings => 'all');
return { '$eval' => 'sub ' . $dp->coderef2text($o) };
}
if (ref $o eq 'ARRAY') {
local $_;
return [ map(ref($_)?$self->deparse_object($_):$_, @$o) ];
}
if (ref $o eq 'SCALAR' or ref $o eq 'REF') {
local $_;
return { '$scalar' => $self->deparse_object($$o) };
}
if (ref $o eq 'HASH') {
local $_;
return { map { $_ => ref($o->{$_})?$self->deparse_object($o->{$_}):$o->{$_} } keys %$o };
}
if (blessed($o) and $o->isa('Type::Tiny')) {
if ($o->has_library and not $o->is_anon and $o->library->has_type($o->name)) {
require B;
return { '$eval' => sprintf('do { require %s; %s->get_type(%s) }', $o->library, B::perlstring($o->library), B::perlstring($o->name)) };
}
else {
require Carp;
Carp::croak('Very limited support for serializing Type::Tiny objects right now');
}
}
if (blessed($o) and $self->convert_blessed and $o->can('TO_JSON')) {
my $unblessed = $o->TO_JSON;
return $self->deparse_object($unblessed);
}
$o;
}
sub DESTROY { }
1;
__END__
=pod
=encoding utf-8
=head1 NAME
JSON::Eval - eval Perl code found in JSON
=head1 SYNOPSIS
my $encoder = JSON::Eval->new();
my $object = {
coderef => sub { 2 + shift },
scalarref => do { my $x = 40; \$x },
};
( run in 1.092 second using v1.01-cache-2.11-cpan-39bf76dae61 )