File Coverage

blib/lib/JSON/ON.pm
Criterion Covered Total %
statement 63 68 92.6
branch 13 22 59.0
condition 5 14 35.7
subroutine 17 20 85.0
pod 6 6 100.0
total 104 130 80.0


line stmt bran cond sub pod time code
1             package JSON::ON;
2             $VERSION = v0.0.3;
3              
4 2     2   35333 use warnings;
  2         6  
  2         69  
5 2     2   11 use strict;
  2         2  
  2         66  
6 2     2   12 use Carp;
  2         12  
  2         180  
7              
8             =head1 NAME
9              
10             JSON::ON - javascript object notation object notator
11              
12             =head1 SYNOPSIS
13              
14             This module serializes and deserializes blessed references with JSON.
15              
16             use JSON::ON;
17              
18             my $stuff = {whatever => What::Ever->new};
19             my $j = JSON::ON->new;
20             my $enc = $j->encode($stuff);
21              
22             # elsewhere...
23             my $j = JSON::ON->new;
24             my $dec = $j->decode($enc);
25             $dec->{whatever}->amethod;
26              
27             =head2 Making Sausage
28              
29             The encode() method installs a local UNIVERSAL::TO_JSON() which simply
30             un-blesses HASH, ARRAY, and SCALAR references. Similarly, the decoding
31             has a hook which simply blesses them back into their class. This leaves
32             edge cases for inside-out objects and code references, but ...
33              
34             =head2 Implementation
35              
36             A special token is embedded in the JSON whenever an object appears (it is ugly, but highly unlikely to appear in a regular JSON document.)
37              
38             This is intended more as an opaque transport mechanism than as
39             human-readable JSON.
40              
41             =cut
42              
43 2     2   1936 use Class::Accessor::Classy;
  2         11387  
  2         18  
44             with 'new';
45             ro 'j';
46             ro 'module_handler';
47 2     2   510 no Class::Accessor::Classy;
  2         5  
  2         12  
48              
49 2     2   608 use Scalar::Util qw(reftype);
  2         9  
  2         280  
50              
51             use constant JSON_CLASS =>
52             eval {require JSON::XS; 'JSON::XS'} ||
53 2   33 2   11 do {require JSON; 'JSON'};
  2         3  
  2         6  
54              
55 2         1598 use constant _json_marker =>
56             "i = sqrt(-1); " . "\b"x14 .
57 2     2   14 "# object =>;#<";
  2         4  
58              
59             =head1 Constructor
60              
61             =head2 new
62              
63             my $j = JSON::ON->new(%args);
64              
65             =over
66              
67             =item j
68              
69             Optional. The JSON object.
70              
71             =item module_handler
72              
73             Optional. A callback for handling modules found in decode(). The
74             default is to warn if a module has no VERSION method (which is a good
75             indicator that it is not loaded.) Setting this to undef() will ignore
76             all modules.
77              
78             module_handler => sub {
79             foreach my $module (@_) {
80             unless($module->can("VERSION")) {
81             (my $pm = $module) =~ s{::}{/}g;
82             eval {require "$pm.pm"};
83             die $@ if($@ and $@ !~ m/^Can't locate $pm in \@INC /);
84             }
85             }
86             },
87              
88             =back
89              
90             =cut
91              
92             sub new {
93 1     1 1 26 my $self = shift->SUPER::new(@_);
94             my $j = $self->{j} ||=
95             JSON_CLASS()->new
96             ->convert_blessed(1)
97             ->filter_json_single_key_object(_json_marker(), sub {
98 3 50 50 3   12 if(my $m = $self->{_modules}) {($m->{$_[0]->[1]} ||= 0)++}
  3         18  
99 3 100       35 return bless($_[0]->[2] eq 'SCALAR' ?
100             \ ($_[0]->[0]) : $_[0]->[0], $_[0]->[1]);
101 1   33     51 });
102             $self->{module_handler} = sub {
103 0     0   0 foreach my $mod (@_) {
104 0 0       0 carp("$mod is not loaded") unless $mod->can('VERSION');
105             }
106 1 50       8 } unless exists $self->{module_handler};
107 1         4 $self;
108             } # new ################################################################
109              
110             # unbless simple reference types
111             sub _obj_to_hash {
112 3     3   13 my $rt = reftype($_[0]);
113 3         7 my $ref = ref($_[0]);
114 1         3 my $obj = $rt eq 'HASH'
115 1         4 ? {%{$_[0]}} : $rt eq 'ARRAY'
116 3 100       12 ? [@{$_[0]}] : ${$_[0]};
  1 100       4  
117 3         40 return {_json_marker() => [$obj, $ref, $rt]};
118             } # _obj_to_hash #######################################################
119              
120             =head1 Methods
121              
122             =head2 encoder
123              
124             $j->encoder->($data);
125              
126             =cut
127              
128             sub encoder {
129 1     1 1 3 my $self = shift;
130              
131 1         3 my $j = $self->{j};
132 1 50       13 my $encode = $j->can('encode') or die;
133             # suppress 'once'
134 1         4 local *UNIVERSAL::TO_JSON = \&_obj_to_hash;
135             return sub {
136 1     1   4 local *UNIVERSAL::TO_JSON = \&_obj_to_hash;
137             # TODO would goto be faster?
138 1         2 my $x = eval {$encode->($j, $_[0])};
  1         27  
139 1 50       5 $@ and croak($@);
140 1         5 return($x);
141 1         9 };
142             }
143              
144             =head2 decoder
145              
146             $j->decoder->($data);
147              
148             =cut
149              
150             sub decoder {
151 1     1 1 2 my $self = shift;
152              
153 1         3 my $j = $self->{j};
154 1 50       27 my $decode = $j->can('decode') or die;
155 1         40 my $hook = $self->module_handler;
156             return $hook ? sub {
157 1     1   4 my $mod = local $self->{_modules} = {};
158 1         11 my $res = $decode->($j, $_[0]);
159 1 50       8 $hook->(keys %$mod) if(%$mod);
160 1         8 return $res;
161             }
162 1 50   0   14 : sub { return $decode->($j, $_[0]); };
  0         0  
163             }
164              
165             =head2 subs
166              
167             Convenience method. Returns $j->decoder, $j->encoder subrefs.
168              
169             my ($dec, $enc) = $j->subs;
170              
171             =cut
172              
173             sub subs {
174 0     0 1 0 my $self = shift;
175 0         0 return($self->decoder, $self->encoder);
176             } # subs ###############################################################
177              
178             =head2 encode
179              
180             my $enc = $j->encode($stuff);
181              
182             =cut
183              
184             sub encode {
185 1     1 1 454 my ($self, $what) = @_;
186              
187 1   33     9 my $e = $self->{_encoder} ||= $self->encoder;
188 1         4 return $e->($what);
189             } # encode #############################################################
190              
191             =head2 decode
192              
193             my $dec = $j->decode($enc);
194              
195             =cut
196              
197             sub decode {
198 1     1 1 283 my ($self, $what) = @_;
199              
200 1   33     9 my $d = $self->{_decoder} ||= $self->decoder;
201 1         4 return $d->($what);
202             } # decode #############################################################
203              
204             =head1 AUTHOR
205              
206             Eric Wilhelm @
207              
208             http://scratchcomputing.com/
209              
210             =head1 BUGS
211              
212             If you found this module on CPAN, please report any bugs or feature
213             requests through the web interface at L. I will be
214             notified, and then you'll automatically be notified of progress on your
215             bug as I make changes.
216              
217             If you pulled this development version from my /svn/, please contact me
218             directly.
219              
220             =head1 COPYRIGHT
221              
222             Copyright (C) 2010-2013 Eric L. Wilhelm, All Rights Reserved.
223              
224             =head1 NO WARRANTY
225              
226             Absolutely, positively NO WARRANTY, neither express or implied, is
227             offered with this software. You use this software at your own risk. In
228             case of loss, no person or entity owes you anything whatsoever. You
229             have been warned.
230              
231             =head1 LICENSE
232              
233             This program is free software; you can redistribute it and/or modify it
234             under the same terms as Perl itself.
235              
236             =cut
237              
238             # vi:ts=2:sw=2:et:sta
239             1;