File Coverage

blib/lib/JSON/Any.pm
Criterion Covered Total %
statement 153 212 72.1
branch 50 76 65.7
condition 11 15 73.3
subroutine 22 22 100.0
pod 7 7 100.0
total 243 332 73.1


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