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.40-5-g0826677
2              
3 12     12   3407541 use warnings;
  12         19  
  12         697  
4 12     12   48 use strict;
  12         33  
  12         472  
5              
6             our $VERSION = '1.41';
7              
8 12     12   56 use Carp qw(croak carp);
  12         20  
  12         891  
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   51 use constant HANDLER => 0;
  12         23  
  12         820  
17 12     12   47 use constant ENCODER => 1;
  12         14  
  12         425  
18 12     12   41 use constant DECODER => 2;
  12         13  
  12         355  
19 12     12   1573 use constant UTF8 => 3;
  12         17  
  12         15651  
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         7 get_true => sub { return JSON::PP::true(); },
63 2         8 get_false => sub { return JSON::PP::false(); },
64             create_object => sub {
65 3         7 my ( $self, $conf ) = @_;
66 3         14 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         8 local $conf->{utf8} = !$conf->{utf8}; # it means the opposite
92 3         30 my $obj = $handler->new;
93              
94 3         33 for my $mutator (@params) {
95 69 100       102 next unless exists $conf->{$mutator};
96 3         64 $obj = $obj->$mutator( $conf->{$mutator} );
97             }
98              
99 3         16 $self->[ENCODER] = 'encode';
100 3         6 $self->[DECODER] = 'decode';
101 3         8 $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         28 my ( $self, $conf ) = @_;
157              
158 14         58 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         33 local $conf->{utf8} = !$conf->{utf8}; # it means the opposite
178              
179 14         53 my $obj = $handler->new;
180 14         27 for my $mutator (@params) {
181 224 100       295 next unless exists $conf->{$mutator};
182 27         67 $obj = $obj->$mutator( $conf->{$mutator} );
183             }
184 14         33 $self->[ENCODER] = 'encode';
185 14         16 $self->[DECODER] = 'decode';
186 14         39 $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   522 );
207              
208             # JSON.pm v2 has the same API as JSON::PP
209 12         47 $conf{json_2} = { %{ $conf{json_pp} } };
  12         71  
210 12         35 $conf{json_2}{get_true} = sub { return JSON::true(); };
  0         0  
211 12         25 $conf{json_2}{get_false} = sub { return JSON::false(); };
  0         0  
212             {
213 12         16 my $create = $conf{json_2}{create_object};
  12         20  
214             $conf{json_2}{create_object} = sub {
215 0         0 JSON->import( '-support_by_pp', '-no_export' );
216 0         0 goto &$create;
217 12         48 };
218             }
219              
220             # JSON.pm v3 and v4 are the same as v2
221 12         16 $conf{json_3} = { %{ $conf{json_2} } };
  12         40  
222 12         25 $conf{json_4} = { %{ $conf{json_3} } };
  12         37  
223              
224             # Cpanel::JSON::XS is a fork of JSON::XS (currently)
225 12         24 $conf{cpanel_json_xs} = { %{ $conf{json_xs_2} } };
  12         37  
226 12         32 $conf{cpanel_json_xs}{get_true} = sub { return Cpanel::JSON::XS::true(); };
  4         13  
227 12         63 $conf{cpanel_json_xs}{get_false} = sub { return Cpanel::JSON::XS::false(); };
  4         14  
228              
229             # JSON::XS 3 is almost the same as JSON::XS 2
230 12         15 $conf{json_xs_3} = { %{ $conf{json_xs_2} } };
  12         50  
231 12         26 $conf{json_xs_3}{get_true} = sub { return Types::Serialiser::true(); };
  0         0  
232 12         26 $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         27 $conf{json_xs_4} = { %{ $conf{json_xs_3} } };
  12         1095  
236             }
237              
238             sub _make_key {
239 42     42   59 my $handler = shift;
240 42         197 ( my $key = lc($handler) ) =~ s/::/_/g;
241 42 50 33     194 if ( 'json_xs' eq $key || 'json' eq $key ) {
242 12     12   99 no strict 'refs';
  12         24  
  12         17645  
243 0         0 $key .= "_" . ( split /\./, ${"$handler\::VERSION"} )[0];
  0         0  
244             }
245 42         74 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   96 my ($testmod) = @_;
253 56 100       149 return 'Cpanel::JSON::XS' if $testmod eq 'CPANEL';
254 44 100       103 return 'JSON' if $testmod eq 'JSON';
255 32         64 return "JSON::$testmod";
256             }
257              
258             sub _try_loading {
259 30     30   50 my @order = @_;
260 30         37 ( $handler, $encoder, $decoder ) = ();
261 30         47 foreach my $mod (@order) {
262 35         534 my $testmod = _module_name($mod);
263 35 100       1809 if (eval "require $testmod; 1") {
264 13         23 $handler = $testmod;
265 13         34 my $key = _make_key($handler);
266 13 50       87 next unless exists $conf{$key};
267 13         34 $encoder = $conf{$key}->{encoder};
268 13         37 $decoder = $conf{$key}->{decoder};
269 13         45 last;
270             }
271             }
272 30         928 return ( $handler, $encoder, $decoder );
273             }
274              
275             sub import {
276 29     29   1569124 my $class = shift;
277 29         66 my @order = @_;
278              
279 29         53 ( $handler, $encoder, $decoder ) = ();
280              
281             @order = split /\s/, $ENV{JSON_ANY_ORDER}
282 29 100 100     126 if !@order and $ENV{JSON_ANY_ORDER};
283              
284 29 100       62 if (@order) {
285 23         57 ( $handler, $encoder, $decoder ) = _try_loading(@order);
286 23 50 66     114 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         13 ( $handler, $encoder, $decoder ) = _try_loading(@default);
295 6 100       25 unless ($handler) {
296 1         2 ( $handler, $encoder, $decoder ) = _try_loading(@deprecated);
297 1 50       3 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       59 unless ($handler) {
305 16 100       53 croak "Couldn't find a JSON package. Need ", _module_name_list(@order ? @order : @default);
306             }
307 13 50       28 croak "Couldn't find a working decoder method (but found handler $handler ", $handler->VERSION, ")." unless $decoder;
308 13 50       5222 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   38 my @list = map _module_name($_), @_;
313 16         37 my $last = pop @list;
314             return (@list
315 16 100       2872 ? (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 unicode strings in the input data structure will be preserved as such.
447             #pod Note that this is the B of the meaning of the underlying C option
448             #pod in many backends!
449             #pod
450             #pod Also note that the C parameter is recognised by all the modules
451             #pod that throw exceptions when a blessed reference is given them meaning that
452             #pod setting it to true works for all modules. Of course, that means that you
453             #pod cannot set it to false intentionally in order to always get such exceptions.
454             #pod
455             #pod The actual output will vary, for example L will encode and decode
456             #pod unicode chars (the resulting JSON is not unicode) whereas L will emit
457             #pod unicode JSON.
458             #pod
459             #pod =cut
460              
461             sub new {
462 17     17 1 4987 my $class = shift;
463 17         33 my $self = bless [], $class;
464 17         32 my $key = _make_key($handler);
465 17 50       52 if ( my $creator = $conf{$key}->{create_object} ) {
466 17         20 my @config;
467             # undocumented! and yet, people are using this...
468 17 100       51 if ( $ENV{JSON_ANY_CONFIG} ) {
469 4         20 push @config, map split(/=/, $_), split(/,\s*/, $ENV{JSON_ANY_CONFIG});
470             }
471 17         35 push @config, @_;
472 17         81 $creator->( $self, my $conf = {@config} );
473 17         42 $self->[UTF8] = $conf->{utf8};
474             }
475 17         83 return $self;
476             }
477              
478             #pod =head2 C
479             #pod
480             #pod Takes no arguments, returns a string indicating which JSON Module is in use.
481             #pod
482             #pod =cut
483              
484             sub handlerType {
485 1     1 1 5 my $class = shift;
486 1         2 $handler;
487             }
488              
489             #pod =head2 C
490             #pod
491             #pod Takes no arguments, if called on an object returns the internal JSON::*
492             #pod object in use. Otherwise returns the JSON::* package we are using for
493             #pod class methods.
494             #pod
495             #pod =cut
496              
497             sub handler {
498 11     11 1 47 my $self = shift;
499 11 50       20 if ( ref $self ) {
500 11         20 return $self->[HANDLER];
501             }
502 0         0 return $handler;
503             }
504              
505             #pod =head2 C
506             #pod
507             #pod Takes no arguments, returns the special value that the internal JSON
508             #pod object uses to map to a JSON C boolean.
509             #pod
510             #pod =cut
511              
512             sub true {
513 6     6 1 2976 my $key = _make_key($handler);
514 6         18 return $conf{$key}->{get_true}->();
515             }
516              
517             #pod =head2 C
518             #pod
519             #pod Takes no arguments, returns the special value that the internal JSON
520             #pod object uses to map to a JSON C boolean.
521             #pod
522             #pod =cut
523              
524             sub false {
525 6     6 1 3174 my $key = _make_key($handler);
526 6         18 return $conf{$key}->{get_false}->();
527             }
528              
529             #pod =head2 C
530             #pod
531             #pod Takes a single argument, a hashref to be converted into JSON.
532             #pod It returns the JSON text in a scalar.
533             #pod
534             #pod =cut
535              
536             sub objToJson {
537 52     52 1 490029 my $self = shift;
538 52         71 my $obj = shift;
539 52 50       95 croak 'must provide object to convert' unless defined $obj;
540              
541 52         68 my $json;
542              
543 52 100       102 if ( ref $self ) {
544 26         24 my $method;
545 26 50       45 unless ( ref $self->[ENCODER] ) {
546 26 50       38 croak "No $handler Object created!"
547             unless exists $self->[HANDLER];
548 26         83 $method = $self->[HANDLER]->can( $self->[ENCODER] );
549 26 50       47 croak "$handler can't execute $self->[ENCODER]" unless $method;
550             }
551             else {
552 0         0 $method = $self->[ENCODER];
553             }
554 26         663 $json = $self->[HANDLER]->$method($obj);
555             }
556             else {
557 26         136 $json = $handler->can($encoder)->($obj);
558             }
559              
560 50 100 100     2840 utf8::decode($json)
    100 66        
561             if ( ref $self ? $self->[UTF8] : $UTF8 )
562             and !utf8::is_utf8($json)
563             and utf8::valid($json);
564 50         128 return $json;
565             }
566              
567             #pod =head2 C
568             #pod
569             #pod =head2 C
570             #pod
571             #pod =head2 C
572             #pod
573             #pod Aliases for C, can be used interchangeably, regardless of the
574             #pod underlying JSON module.
575             #pod =cut
576              
577             *to_json = \&objToJson;
578             *Dump = \&objToJson;
579             *encode = \&objToJson;
580              
581             #pod =head2 C
582             #pod
583             #pod Takes a single argument, a string of JSON text to be converted
584             #pod back into a hashref.
585             #pod
586             #pod =cut
587              
588             sub jsonToObj {
589 40     40 1 25215 my $self = shift;
590 40         49 my $obj = shift;
591 40 50       109 croak 'must provide json to convert' unless defined $obj;
592              
593             # some handlers can't parse single booleans (I'm looking at you DWIW)
594 40 100       145 if ( $obj =~ /^(true|false)$/ ) {
595 2         10 return $self->$1;
596             }
597              
598 38 100       59 if ( ref $self ) {
599 17         18 my $method;
600 17 50       29 unless ( ref $self->[DECODER] ) {
601 17 50       26 croak "No $handler Object created!"
602             unless exists $self->[HANDLER];
603 17         61 $method = $self->[HANDLER]->can( $self->[DECODER] );
604 17 50       28 croak "$handler can't execute $self->[DECODER]" unless $method;
605             }
606             else {
607 0         0 $method = $self->[DECODER];
608             }
609 17         74 return $self->[HANDLER]->$method($obj);
610             }
611 21         143 $handler->can($decoder)->($obj);
612             }
613              
614             #pod =head2 C
615             #pod
616             #pod =head2 C
617             #pod
618             #pod =head2 C
619             #pod
620             #pod Aliases for C, can be used interchangeably, regardless of the
621             #pod underlying JSON module.
622             #pod
623             #pod =cut
624              
625             *from_json = \&jsonToObj;
626             *Load = \&jsonToObj;
627             *decode = \&jsonToObj;
628              
629             {
630 12     12   115 no strict 'refs';
  12         17  
  12         1027  
631             delete @{__PACKAGE__.'::'}{qw(croak carp)};
632             }
633              
634             1;
635              
636             __END__