File Coverage

blib/lib/JSON/Any.pm
Criterion Covered Total %
statement 158 218 72.4
branch 50 76 65.7
condition 11 15 73.3
subroutine 22 22 100.0
pod 7 7 100.0
total 248 338 73.3


line stmt bran cond sub pod time code
1             package JSON::Any; # git description: v1.39-25-gd13a0fd
2              
3 12     12   420493 use warnings;
  12         68  
  12         397  
4 12     12   72 use strict;
  12         29  
  12         432  
5              
6             our $VERSION = '1.40';
7              
8 12     12   60 use Carp qw(croak carp);
  12         22  
  12         834  
9              
10             # ABSTRACT: (DEPRECATED) Wrapper Class for the various JSON classes
11             # KEYWORDS: json serialization serialisation wrapper abstraction
12              
13             our $UTF8;
14              
15             my ( %conf, $handler, $encoder, $decoder );
16 12     12   82 use constant HANDLER => 0;
  12         22  
  12         1099  
17 12     12   83 use constant ENCODER => 1;
  12         17  
  12         667  
18 12     12   76 use constant DECODER => 2;
  12         21  
  12         631  
19 12     12   65 use constant UTF8 => 3;
  12         20  
  12         16616  
20              
21             BEGIN {
22             %conf = (
23             json_1 => {
24             encoder => 'objToJson',
25             decoder => 'jsonToObj',
26 0         0 get_true => sub { return JSON::True(); },
27 0         0 get_false => sub { return JSON::False(); },
28             create_object => sub {
29 0         0 require JSON;
30 0         0 my ( $self, $conf ) = @_;
31 0         0 my @params = qw(
32             autoconv
33             skipinvalid
34             execcoderef
35             pretty
36             indent
37             delimiter
38             keysort
39             convblessed
40             selfconvert
41             singlequote
42             quoteapos
43             unmapping
44             barekey
45             );
46             my $obj =
47 0         0 $handler->new( utf8 => $conf->{utf8} ); ## constructor only
48              
49 0         0 for my $mutator (@params) {
50 0 0       0 next unless exists $conf->{$mutator};
51 0         0 $obj = $obj->$mutator( $conf->{$mutator} );
52             }
53              
54 0         0 $self->[ENCODER] = 'objToJson';
55 0         0 $self->[DECODER] = 'jsonToObj';
56 0         0 $self->[HANDLER] = $obj;
57             },
58             },
59             json_pp => {
60             encoder => 'encode_json',
61             decoder => 'decode_json',
62 2         5 get_true => sub { return JSON::PP::true(); },
63 2         5 get_false => sub { return JSON::PP::false(); },
64             create_object => sub {
65 3         9 my ( $self, $conf ) = @_;
66 3         18 my @params = qw(
67             ascii
68             latin1
69             utf8
70             pretty
71             indent
72             space_before
73             space_after
74             relaxed
75             canonical
76             allow_nonref
77             allow_blessed
78             convert_blessed
79             filter_json_object
80             shrink
81             max_depth
82             max_size
83             loose
84             allow_bignum
85             allow_barekey
86             allow_singlequote
87             escape_slash
88             indent_length
89             sort_by
90             );
91 3         9 local $conf->{utf8} = !$conf->{utf8}; # it means the opposite
92 3         12 my $obj = $handler->new;
93              
94 3         36 for my $mutator (@params) {
95 69 100       160 next unless exists $conf->{$mutator};
96 3         69 $obj = $obj->$mutator( $conf->{$mutator} );
97             }
98              
99 3         16 $self->[ENCODER] = 'encode';
100 3         7 $self->[DECODER] = 'decode';
101 3         25 $self->[HANDLER] = $obj;
102             },
103             },
104             json_dwiw => {
105             encoder => 'to_json',
106             decoder => 'from_json',
107 0         0 get_true => sub { return JSON::DWIW->true; },
108 0         0 get_false => sub { return JSON::DWIW->false; },
109             create_object => sub {
110 0         0 my ( $self, $conf ) = @_;
111 0         0 my @params = qw(bare_keys);
112 0 0       0 croak "JSON::DWIW does not support utf8" if $conf->{utf8};
113 0         0 $self->[ENCODER] = 'to_json';
114 0         0 $self->[DECODER] = 'from_json';
115             $self->[HANDLER] =
116 0         0 $handler->new( { map +($_ => $conf->{$_}), @params } );
117             },
118             },
119             json_xs_1 => {
120             encoder => 'to_json',
121             decoder => 'from_json',
122 0         0 get_true => sub { return \1; },
123 0         0 get_false => sub { return \0; },
124             create_object => sub {
125 0         0 my ( $self, $conf ) = @_;
126              
127 0         0 my @params = qw(
128             ascii
129             utf8
130             pretty
131             indent
132             space_before
133             space_after
134             canonical
135             allow_nonref
136             shrink
137             max_depth
138             );
139              
140 0         0 my $obj = $handler->new;
141 0         0 for my $mutator (@params) {
142 0 0       0 next unless exists $conf->{$mutator};
143 0         0 $obj = $obj->$mutator( $conf->{$mutator} );
144             }
145 0         0 $self->[ENCODER] = 'encode';
146 0         0 $self->[DECODER] = 'decode';
147 0         0 $self->[HANDLER] = $obj;
148             },
149             },
150             json_xs_2 => {
151             encoder => 'encode_json',
152             decoder => 'decode_json',
153 0         0 get_true => sub { return JSON::XS::true(); },
154 0         0 get_false => sub { return JSON::XS::false(); },
155             create_object => sub {
156 14         31 my ( $self, $conf ) = @_;
157              
158 14         54 my @params = qw(
159             ascii
160             latin1
161             utf8
162             pretty
163             indent
164             space_before
165             space_after
166             relaxed
167             canonical
168             allow_nonref
169             allow_blessed
170             convert_blessed
171             filter_json_object
172             shrink
173             max_depth
174             max_size
175             );
176              
177 14         39 local $conf->{utf8} = !$conf->{utf8}; # it means the opposite
178              
179 14         57 my $obj = $handler->new;
180 14         36 for my $mutator (@params) {
181 224 100       390 next unless exists $conf->{$mutator};
182 27         76 $obj = $obj->$mutator( $conf->{$mutator} );
183             }
184 14         41 $self->[ENCODER] = 'encode';
185 14         25 $self->[DECODER] = 'decode';
186 14         56 $self->[HANDLER] = $obj;
187             },
188             },
189             json_syck => {
190             encoder => 'Dump',
191             decoder => 'Load',
192             get_true => sub {
193 0         0 croak "JSON::Syck does not support special boolean values";
194             },
195             get_false => sub {
196 0         0 croak "JSON::Syck does not support special boolean values";
197             },
198             create_object => sub {
199 0         0 my ( $self, $conf ) = @_;
200 0 0       0 croak "JSON::Syck does not support utf8" if $conf->{utf8};
201 0         0 $self->[ENCODER] = sub { Dump(@_) };
  0         0  
202 0         0 $self->[DECODER] = sub { Load(@_) };
  0         0  
203 0         0 $self->[HANDLER] = 'JSON::Syck';
204             }
205             },
206 12     12   728 );
207              
208             # JSON.pm v2 has the same API as JSON::PP
209 12         47 $conf{json_2} = { %{ $conf{json_pp} } };
  12         64  
210 12         44 $conf{json_2}{get_true} = sub { return JSON::true(); };
  0         0  
211 12         42 $conf{json_2}{get_false} = sub { return JSON::false(); };
  0         0  
212             {
213 12         31 my $create = $conf{json_2}{create_object};
  12         32  
214             $conf{json_2}{create_object} = sub {
215 0         0 JSON->import( '-support_by_pp', '-no_export' );
216 0         0 goto &$create;
217 12         61 };
218             }
219              
220             # JSON.pm v3 and v4 are the same as v2
221 12         26 $conf{json_3} = { %{ $conf{json_2} } };
  12         59  
222 12         25 $conf{json_4} = { %{ $conf{json_3} } };
  12         63  
223              
224             # Cpanel::JSON::XS is a fork of JSON::XS (currently)
225 12         23 $conf{cpanel_json_xs} = { %{ $conf{json_xs_2} } };
  12         35  
226 12         70 $conf{cpanel_json_xs}{get_true} = sub { return Cpanel::JSON::XS::true(); };
  4         10  
227 12         47 $conf{cpanel_json_xs}{get_false} = sub { return Cpanel::JSON::XS::false(); };
  4         11  
228              
229             # JSON::XS 3 is almost the same as JSON::XS 2
230 12         142 $conf{json_xs_3} = { %{ $conf{json_xs_2} } };
  12         56  
231 12         47 $conf{json_xs_3}{get_true} = sub { return Types::Serialiser::true(); };
  0         0  
232 12         40 $conf{json_xs_3}{get_false} = sub { return Types::Serialiser::false(); };
  0         0  
233              
234             # JSON::XS v4 is the same as v3
235 12         18 $conf{json_xs_4} = { %{ $conf{json_xs_3} } };
  12         1159  
236             }
237              
238             sub _make_key {
239 42     42   75 my $handler = shift;
240 42         257 ( my $key = lc($handler) ) =~ s/::/_/g;
241 42 50 33     224 if ( 'json_xs' eq $key || 'json' eq $key ) {
242 12     12   92 no strict 'refs';
  12         20  
  12         19891  
243 0         0 $key .= "_" . ( split /\./, ${"$handler\::VERSION"} )[0];
  0         0  
244             }
245 42         93 return $key;
246             }
247              
248             my @default = qw(CPANEL XS PP JSON DWIW);
249             my @deprecated = qw(Syck);
250              
251             sub _module_name {
252 56     56   127 my ($testmod) = @_;
253 56 100       146 return 'Cpanel::JSON::XS' if $testmod eq 'CPANEL';
254 44 100       105 return 'JSON' if $testmod eq 'JSON';
255 32         100 return "JSON::$testmod";
256             }
257              
258             sub _try_loading {
259 30     30   65 my @order = @_;
260 30         49 ( $handler, $encoder, $decoder ) = ();
261 30         63 foreach my $mod (@order) {
262 35         591 my $testmod = _module_name($mod);
263 35 100       1890 if (eval "require $testmod; 1") {
264 13         43 $handler = $testmod;
265 13         35 my $key = _make_key($handler);
266 13 50       43 next unless exists $conf{$key};
267 13         41 $encoder = $conf{$key}->{encoder};
268 13         23 $decoder = $conf{$key}->{decoder};
269 13         30 last;
270             }
271             }
272 30         1453 return ( $handler, $encoder, $decoder );
273             }
274              
275             sub import {
276 29     29   20333 my $class = shift;
277 29         110 my @order = @_;
278              
279 29         65 ( $handler, $encoder, $decoder ) = ();
280              
281             @order = split /\s/, $ENV{JSON_ANY_ORDER}
282 29 100 100     190 if !@order and $ENV{JSON_ANY_ORDER};
283              
284 29 100       89 if (@order) {
285 23         57 ( $handler, $encoder, $decoder ) = _try_loading(@order);
286 23 50 66     124 if ( $handler && grep "JSON::$_" eq $handler, @deprecated ) {
287 0         0 my @upgrade_to = grep { my $mod = $_; !grep { $mod eq $_ } @deprecated } @order;
  0         0  
  0         0  
  0         0  
288 0 0       0 @upgrade_to = @default if not @upgrade_to;
289 0         0 carp "Found deprecated package $handler. Please upgrade to ",
290             _module_name_list(@upgrade_to);
291             }
292             }
293             else {
294 6         16 ( $handler, $encoder, $decoder ) = _try_loading(@default);
295 6 100       17 unless ($handler) {
296 1         5 ( $handler, $encoder, $decoder ) = _try_loading(@deprecated);
297 1 50       4 if ($handler) {
298 0         0 carp "Found deprecated package $handler. Please upgrade to ",
299             _module_name_list(@default);
300             }
301             }
302             }
303              
304 29 100       96 unless ($handler) {
305 16 100       77 croak "Couldn't find a JSON package. Need ", _module_name_list(@order ? @order : @default);
306             }
307 13 50       36 croak "Couldn't find a working decoder method (but found handler $handler ", $handler->VERSION, ")." unless $decoder;
308 13 50       6104 croak "Couldn't find a working encoder method (but found handler $handler ", $handler->VERSION, ")." unless $encoder;
309             }
310              
311             sub _module_name_list {
312 16     16   55 my @list = map _module_name($_), @_;
313 16         33 my $last = pop @list;
314             return (@list
315 16 100       2577 ? (join(', ' => @list), " or $last")
316             : $last
317             );
318             }
319              
320             #pod =head1 SYNOPSIS
321             #pod
322             #pod use JSON::Any;
323             #pod my $j = JSON::Any->new;
324             #pod my $json = $j->objToJson({foo=>'bar', baz=>'quux'});
325             #pod my $obj = $j->jsonToObj($json);
326             #pod
327             #pod =head1 DEPRECATION NOTICE
328             #pod
329             #pod The original need for L has been solved (quite some time ago
330             #pod actually). If you're producing new code it is recommended to use L which
331             #pod will optionally use L for speed purposes.
332             #pod
333             #pod JSON::Any will continue to be maintained for compatibility with existing code
334             #pod (as well as for rare cases where you want L as a backend),
335             #pod but for new code you should strongly consider using L instead.
336             #pod
337             #pod For more information about the various options and which are preferred, see
338             #pod L.
339             #pod
340             #pod =head1 DESCRIPTION
341             #pod
342             #pod This module tries to provide a coherent API to bring together the various JSON
343             #pod modules currently on CPAN. This module will allow you to code to any JSON API
344             #pod and have it work regardless of which JSON module is actually installed.
345             #pod
346             #pod use JSON::Any;
347             #pod
348             #pod my $j = JSON::Any->new;
349             #pod
350             #pod $json = $j->objToJson({foo=>'bar', baz=>'quux'});
351             #pod $obj = $j->jsonToObj($json);
352             #pod
353             #pod or
354             #pod
355             #pod $json = $j->encode({foo=>'bar', baz=>'quux'});
356             #pod $obj = $j->decode($json);
357             #pod
358             #pod or
359             #pod
360             #pod $json = $j->Dump({foo=>'bar', baz=>'quux'});
361             #pod $obj = $j->Load($json);
362             #pod
363             #pod or
364             #pod
365             #pod $json = $j->to_json({foo=>'bar', baz=>'quux'});
366             #pod $obj = $j->from_json($json);
367             #pod
368             #pod or without creating an object:
369             #pod
370             #pod $json = JSON::Any->objToJson({foo=>'bar', baz=>'quux'});
371             #pod $obj = JSON::Any->jsonToObj($json);
372             #pod
373             #pod On load, JSON::Any will find a valid JSON module in your @INC by looking
374             #pod for them in this order:
375             #pod
376             #pod Cpanel::JSON::XS
377             #pod JSON::XS
378             #pod JSON::PP
379             #pod JSON
380             #pod JSON::DWIW
381             #pod
382             #pod And loading the first one it finds.
383             #pod
384             #pod You may change the order by specifying it on the C line:
385             #pod
386             #pod use JSON::Any qw(DWIW XS CPANEL JSON PP);
387             #pod
388             #pod Specifying an order that is missing modules will prevent those module from
389             #pod being used:
390             #pod
391             #pod use JSON::Any qw(CPANEL PP); # same as JSON::MaybeXS
392             #pod
393             #pod This will check in that order, and will never attempt to load L,
394             #pod L, or L. This can also be set via the C<$ENV{JSON_ANY_ORDER}>
395             #pod environment variable.
396             #pod
397             #pod L has been deprecated by its author, but in the attempt to still
398             #pod stay relevant as a "Compatibility Layer" JSON::Any still supports it. This support
399             #pod however has been made optional starting with JSON::Any 1.19. In deference to a
400             #pod bug request starting with L 1.20, L and other deprecated modules
401             #pod will still be installed, but only as a last resort and will now include a
402             #pod warning.
403             #pod
404             #pod use JSON::Any qw(Syck XS JSON);
405             #pod
406             #pod or
407             #pod
408             #pod $ENV{JSON_ANY_ORDER} = 'Syck XS JSON';
409             #pod
410             #pod At install time, JSON::Any will attempt to install L as a reasonable
411             #pod fallback if you do not appear have B backends installed on your system.
412             #pod
413             #pod WARNING: If you call JSON::Any with an empty list
414             #pod
415             #pod use JSON::Any ();
416             #pod
417             #pod It will skip the JSON package detection routines and will die loudly that it
418             #pod couldn't find a package.
419             #pod
420             #pod =head1 WARNING
421             #pod
422             #pod L 3.0 or higher has a conflict with any version of L less than 2.90
423             #pod when you use L's C<-support_by_pp> option, which JSON::Any enables by
424             #pod default.
425             #pod
426             #pod This situation should only come up with JSON::Any if you have L 2.61 or
427             #pod lower B L 3.0 or higher installed, and you use L
428             #pod via C<< use JSON::Any qw(JSON); >> or the C environment variable.
429             #pod
430             #pod If you run into an issue where you're getting recursive inheritance errors in a
431             #pod L package, please try upgrading L to 2.90 or higher.
432             #pod
433             #pod =head1 METHODS
434             #pod
435             #pod =head2 C
436             #pod
437             #pod =for stopwords recognised unicode
438             #pod
439             #pod Will take any of the parameters for the underlying system and pass them
440             #pod through. However these values don't map between JSON modules, so, from a
441             #pod portability standpoint this is really only helpful for those parameters that
442             #pod happen to have the same name.
443             #pod
444             #pod The one parameter that is universally supported (to the extent that is
445             #pod supported by the underlying JSON modules) is C. When this parameter is
446             #pod enabled all resulting JSON will be marked as unicode, and all unicode strings
447             #pod in the input data structure will be preserved as such.
448             #pod
449             #pod Also note that the C parameter is recognised by all the modules
450             #pod that throw exceptions when a blessed reference is given them meaning that
451             #pod setting it to true works for all modules. Of course, that means that you
452             #pod cannot set it to false intentionally in order to always get such exceptions.
453             #pod
454             #pod The actual output will vary, for example L will encode and decode
455             #pod unicode chars (the resulting JSON is not unicode) whereas L will emit
456             #pod unicode JSON.
457             #pod
458             #pod =cut
459              
460             sub new {
461 17     17 1 4859 my $class = shift;
462 17         42 my $self = bless [], $class;
463 17         37 my $key = _make_key($handler);
464 17 50       85 if ( my $creator = $conf{$key}->{create_object} ) {
465 17         26 my @config;
466             # undocumented! and yet, people are using this...
467 17 100       44 if ( $ENV{JSON_ANY_CONFIG} ) {
468 4         34 push @config, map split(/=/, $_), split(/,\s*/, $ENV{JSON_ANY_CONFIG});
469             }
470 17         49 push @config, @_;
471 17         92 $creator->( $self, my $conf = {@config} );
472 17         49 $self->[UTF8] = $conf->{utf8};
473             }
474 17         117 return $self;
475             }
476              
477             #pod =head2 C
478             #pod
479             #pod Takes no arguments, returns a string indicating which JSON Module is in use.
480             #pod
481             #pod =cut
482              
483             sub handlerType {
484 1     1 1 5 my $class = shift;
485 1         3 $handler;
486             }
487              
488             #pod =head2 C
489             #pod
490             #pod Takes no arguments, if called on an object returns the internal JSON::*
491             #pod object in use. Otherwise returns the JSON::* package we are using for
492             #pod class methods.
493             #pod
494             #pod =cut
495              
496             sub handler {
497 11     11 1 57 my $self = shift;
498 11 50       28 if ( ref $self ) {
499 11         28 return $self->[HANDLER];
500             }
501 0         0 return $handler;
502             }
503              
504             #pod =head2 C
505             #pod
506             #pod Takes no arguments, returns the special value that the internal JSON
507             #pod object uses to map to a JSON C boolean.
508             #pod
509             #pod =cut
510              
511             sub true {
512 6     6 1 2612 my $key = _make_key($handler);
513 6         20 return $conf{$key}->{get_true}->();
514             }
515              
516             #pod =head2 C
517             #pod
518             #pod Takes no arguments, returns the special value that the internal JSON
519             #pod object uses to map to a JSON C boolean.
520             #pod
521             #pod =cut
522              
523             sub false {
524 6     6 1 2906 my $key = _make_key($handler);
525 6         19 return $conf{$key}->{get_false}->();
526             }
527              
528             #pod =head2 C
529             #pod
530             #pod Takes a single argument, a hashref to be converted into JSON.
531             #pod It returns the JSON text in a scalar.
532             #pod
533             #pod =cut
534              
535             sub objToJson {
536 52     52 1 11508 my $self = shift;
537 52         73 my $obj = shift;
538 52 50       115 croak 'must provide object to convert' unless defined $obj;
539              
540 52         74 my $json;
541              
542 52 100       107 if ( ref $self ) {
543 26         55 my $method;
544 26 50       51 unless ( ref $self->[ENCODER] ) {
545 26 50       52 croak "No $handler Object created!"
546             unless exists $self->[HANDLER];
547 26         101 $method = $self->[HANDLER]->can( $self->[ENCODER] );
548 26 50       63 croak "$handler can't execute $self->[ENCODER]" unless $method;
549             }
550             else {
551 0         0 $method = $self->[ENCODER];
552             }
553 26         126 $json = $self->[HANDLER]->$method($obj);
554             }
555             else {
556 26         208 $json = $handler->can($encoder)->($obj);
557             }
558              
559 50 100 100     2181 utf8::decode($json)
    100 66        
560             if ( ref $self ? $self->[UTF8] : $UTF8 )
561             and !utf8::is_utf8($json)
562             and utf8::valid($json);
563 50         154 return $json;
564             }
565              
566             #pod =head2 C
567             #pod
568             #pod =head2 C
569             #pod
570             #pod =head2 C
571             #pod
572             #pod Aliases for C, can be used interchangeably, regardless of the
573             #pod underlying JSON module.
574             #pod =cut
575              
576             *to_json = \&objToJson;
577             *Dump = \&objToJson;
578             *encode = \&objToJson;
579              
580             #pod =head2 C
581             #pod
582             #pod Takes a single argument, a string of JSON text to be converted
583             #pod back into a hashref.
584             #pod
585             #pod =cut
586              
587             sub jsonToObj {
588 40     40 1 22703 my $self = shift;
589 40         63 my $obj = shift;
590 40 50       103 croak 'must provide json to convert' unless defined $obj;
591              
592             # some handlers can't parse single booleans (I'm looking at you DWIW)
593 40 100       196 if ( $obj =~ /^(true|false)$/ ) {
594 2         9 return $self->$1;
595             }
596              
597 38 100       80 if ( ref $self ) {
598 17         21 my $method;
599 17 50       38 unless ( ref $self->[DECODER] ) {
600 17 50       32 croak "No $handler Object created!"
601             unless exists $self->[HANDLER];
602 17         50 $method = $self->[HANDLER]->can( $self->[DECODER] );
603 17 50       38 croak "$handler can't execute $self->[DECODER]" unless $method;
604             }
605             else {
606 0         0 $method = $self->[DECODER];
607             }
608 17         73 return $self->[HANDLER]->$method($obj);
609             }
610 21         144 $handler->can($decoder)->($obj);
611             }
612              
613             #pod =head2 C
614             #pod
615             #pod =head2 C
616             #pod
617             #pod =head2 C
618             #pod
619             #pod Aliases for C, can be used interchangeably, regardless of the
620             #pod underlying JSON module.
621             #pod
622             #pod =cut
623              
624             *from_json = \&jsonToObj;
625             *Load = \&jsonToObj;
626             *decode = \&jsonToObj;
627              
628             {
629 12     12   97 no strict 'refs';
  12         43  
  12         856  
630             delete @{__PACKAGE__.'::'}{qw(croak carp)};
631             }
632              
633             1;
634              
635             __END__