File Coverage

blib/lib/JSON/Any.pm
Criterion Covered Total %
statement 151 212 71.2
branch 46 76 60.5
condition 11 15 73.3
subroutine 22 22 100.0
pod 7 7 100.0
total 237 332 71.3


line stmt bran cond sub pod time code
1             package JSON::Any;
2             # git description: v1.37-1-g15d51fd
3             $JSON::Any::VERSION = '1.38';
4              
5 10     10   136308 use warnings;
  10         18  
  10         344  
6 10     10   43 use strict;
  10         12  
  10         328  
7 10     10   45 use Carp qw(croak carp);
  10         18  
  10         606  
8 10     10   4760 use namespace::clean;
  10         133721  
  10         50  
9              
10             # ABSTRACT: Wrapper Class for the various JSON classes (DEPRECATED)
11             # KEYWORDS: json serialization serialisation wrapper abstraction
12              
13             our $UTF8;
14              
15             my ( %conf, $handler, $encoder, $decoder );
16 10     10   1715 use constant HANDLER => 0;
  10         19  
  10         534  
17 10     10   48 use constant ENCODER => 1;
  10         15  
  10         403  
18 10     10   52 use constant DECODER => 2;
  10         15  
  10         393  
19 10     10   44 use constant UTF8 => 3;
  10         14  
  10         13638  
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 1         7 JSON->import( '-support_by_pp', '-no_export' );
66 1         1 my ( $self, $conf ) = @_;
67 1         4 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 1         2 local $conf->{utf8} = !$conf->{utf8}; # it means the opposite
93 1         6 my $obj = $handler->new;
94              
95 1         13 for my $mutator (@params) {
96 23 100       40 next unless exists $conf->{$mutator};
97 1         24 $obj = $obj->$mutator( $conf->{$mutator} );
98             }
99              
100 1         5 $self->[ENCODER] = 'encode';
101 1         1 $self->[DECODER] = 'decode';
102 1         3 $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 12         17 my ( $self, $conf ) = @_;
158              
159 12         48 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 12         33 local $conf->{utf8} = !$conf->{utf8}; # it means the opposite
179              
180 12         66 my $obj = $handler->new;
181 12         23 for my $mutator (@params) {
182 192 100       441 next unless exists $conf->{$mutator};
183 25         76 $obj = $obj->$mutator( $conf->{$mutator} );
184             }
185 12         38 $self->[ENCODER] = 'encode';
186 12         18 $self->[DECODER] = 'decode';
187 12         40 $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 10     10   386 );
208              
209             # JSON::PP has the same API as JSON.pm v2
210 10         28 $conf{json_pp} = { %{ $conf{json_2} } };
  10         62  
211 10         72 $conf{json_pp}{get_true} = sub { return JSON::PP::true(); };
  2         6  
212 10         25 $conf{json_pp}{get_false} = sub { return JSON::PP::false(); };
  2         6  
213              
214             # Cpanel::JSON::XS is a fork of JSON::XS (currently)
215 10         12 $conf{cpanel_json_xs} = { %{ $conf{json_xs_2} } };
  10         46  
216 10         26 $conf{cpanel_json_xs}{get_true} = sub { return Cpanel::JSON::XS::true(); };
  4         15  
217 10         23 $conf{cpanel_json_xs}{get_false} = sub { return Cpanel::JSON::XS::false(); };
  4         12  
218              
219             # JSON::XS 3 is almost the same as JSON::XS 2
220 10         14 $conf{json_xs_3} = { %{ $conf{json_xs_2} } };
  10         33  
221 10         28 $conf{json_xs_3}{get_true} = sub { return Types::Serialiser::true(); };
  0         0  
222 10         875 $conf{json_xs_3}{get_false} = sub { return Types::Serialiser::false(); };
  0         0  
223             }
224              
225             sub _make_key {
226 34     34   42 my $handler = shift;
227 34         167 ( my $key = lc($handler) ) =~ s/::/_/g;
228 34 50 33     197 if ( 'json_xs' eq $key || 'json' eq $key ) {
229 10     10   61 no strict 'refs';
  10         12  
  10         15098  
230 0         0 $key .= "_" . ( split /\./, ${"$handler\::VERSION"} )[0];
  0         0  
231             }
232 34         58 return $key;
233             }
234              
235             my @default = qw(CPANEL XS PP JSON DWIW);
236             my @deprecated = qw(Syck);
237              
238             sub _module_name {
239 25     25   34 my ($testmod) = @_;
240 25 100       81 return 'Cpanel::JSON::XS' if $testmod eq 'CPANEL';
241 17 100       42 return 'JSON' if $testmod eq 'JSON';
242 15         49 return "JSON::$testmod";
243             }
244              
245             sub _try_loading {
246 17     17   105 my @order = @_;
247 17         25 ( $handler, $encoder, $decoder ) = ();
248 17         27 foreach my $mod (@order) {
249 17         41 my $testmod = _module_name($mod);
250 17 100       1209 if (eval "require $testmod; 1") {
251 9         19 $handler = $testmod;
252 9         28 my $key = _make_key($handler);
253 9 50       32 next unless exists $conf{$key};
254 9         26 $encoder = $conf{$key}->{encoder};
255 9         14 $decoder = $conf{$key}->{decoder};
256 9         21 last;
257             }
258             }
259 17         78 return ( $handler, $encoder, $decoder );
260             }
261              
262             sub import {
263 17     17   5391 my $class = shift;
264 17         34 my @order = @_;
265              
266 17         30 ( $handler, $encoder, $decoder ) = ();
267              
268 17 100 100     106 @order = split /\s/, $ENV{JSON_ANY_ORDER}
269             if !@order and $ENV{JSON_ANY_ORDER};
270              
271 17 100       53 if (@order) {
272 12         34 ( $handler, $encoder, $decoder ) = _try_loading(@order);
273 12 50 66     51 if ( $handler && grep { "JSON::$_" eq $handler } @deprecated ) {
  4         40  
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 5         16 ( $handler, $encoder, $decoder ) = _try_loading(@default);
282 5 50       17 unless ($handler) {
283 0         0 ( $handler, $encoder, $decoder ) = _try_loading(@deprecated);
284 0 0       0 if ($handler) {
285 0         0 carp "Found deprecated package $handler. Please upgrade to ",
286             _module_name_list(@default);
287             }
288             }
289             }
290              
291 17 100       47 unless ($handler) {
292 8 50       39 croak "Couldn't find a JSON package. Need ", _module_name_list(@order ? @order : @default);
293             }
294 9 50       22 croak "Couldn't find a working decoder method (but found handler $handler ", $handler->VERSION, ")." unless $decoder;
295 9 50       6106 croak "Couldn't find a working encoder method (but found handler $handler ", $handler->VERSION, ")." unless $encoder;
296             }
297              
298             sub _module_name_list {
299 8     8   18 my @list = map { _module_name($_) } @_;
  8         20  
300 8         16 my $last = pop @list;
301             return (@list
302 8 50       1342 ? (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 =over
419             #pod
420             #pod =item C
421             #pod
422             #pod =for :stopwords recognised unicode
423             #pod
424             #pod Will take any of the parameters for the underlying system and pass them
425             #pod through. However these values don't map between JSON modules, so, from a
426             #pod portability standpoint this is really only helpful for those parameters that
427             #pod happen to have the same name.
428             #pod
429             #pod The one parameter that is universally supported (to the extent that is
430             #pod supported by the underlying JSON modules) is C. When this parameter is
431             #pod enabled all resulting JSON will be marked as unicode, and all unicode strings
432             #pod in the input data structure will be preserved as such.
433             #pod
434             #pod Also note that the C parameter is recognised by all the modules
435             #pod that throw exceptions when a blessed reference is given them meaning that
436             #pod setting it to true works for all modules. Of course, that means that you
437             #pod cannot set it to false intentionally in order to always get such exceptions.
438             #pod
439             #pod The actual output will vary, for example L will encode and decode
440             #pod unicode chars (the resulting JSON is not unicode) whereas L will emit
441             #pod unicode JSON.
442             #pod
443             #pod =back
444             #pod
445             #pod =cut
446              
447             sub new {
448 13     13 1 4577 my $class = shift;
449 13         45 my $self = bless [], $class;
450 13         28 my $key = _make_key($handler);
451 13 50       125 if ( my $creator = $conf{$key}->{create_object} ) {
452 13         13 my @config;
453             # undocumented! and yet, people are using this...
454 13 100       36 if ( $ENV{JSON_ANY_CONFIG} ) {
455 2         6 push @config, map { split /=/, $_ } split /,\s*/,
  2         7  
456             $ENV{JSON_ANY_CONFIG};
457             }
458 13         26 push @config, @_;
459 13         53 $creator->( $self, my $conf = {@config} );
460 13         39 $self->[UTF8] = $conf->{utf8};
461             }
462 13         57 return $self;
463             }
464              
465             #pod =over
466             #pod
467             #pod =item C
468             #pod
469             #pod Takes no arguments, returns a string indicating which JSON Module is in use.
470             #pod
471             #pod =back
472             #pod
473             #pod =cut
474              
475             sub handlerType {
476 1     1 1 6 my $class = shift;
477 1         2 $handler;
478             }
479              
480             #pod =over
481             #pod
482             #pod =item C
483             #pod
484             #pod Takes no arguments, if called on an object returns the internal JSON::*
485             #pod object in use. Otherwise returns the JSON::* package we are using for
486             #pod class methods.
487             #pod
488             #pod =back
489             #pod
490             #pod =cut
491              
492             sub handler {
493 3     3 1 13 my $self = shift;
494 3 50       10 if ( ref $self ) {
495 3         7 return $self->[HANDLER];
496             }
497 0         0 return $handler;
498             }
499              
500             #pod =over
501             #pod
502             #pod =item C
503             #pod
504             #pod Takes no arguments, returns the special value that the internal JSON
505             #pod object uses to map to a JSON C boolean.
506             #pod
507             #pod =back
508             #pod
509             #pod =cut
510              
511             sub true {
512 6     6 1 2224 my $key = _make_key($handler);
513 6         47 return $conf{$key}->{get_true}->();
514             }
515              
516             #pod =over
517             #pod
518             #pod =item C
519             #pod
520             #pod Takes no arguments, returns the special value that the internal JSON
521             #pod object uses to map to a JSON C boolean.
522             #pod
523             #pod =back
524             #pod
525             #pod =cut
526              
527             sub false {
528 6     6 1 2705 my $key = _make_key($handler);
529 6         22 return $conf{$key}->{get_false}->();
530             }
531              
532             #pod =over
533             #pod
534             #pod =item C
535             #pod
536             #pod Takes a single argument, a hashref to be converted into JSON.
537             #pod It returns the JSON text in a scalar.
538             #pod
539             #pod =back
540             #pod
541             #pod =cut
542              
543             sub objToJson {
544 34     34 1 5582 my $self = shift;
545 34         40 my $obj = shift;
546 34 50       74 croak 'must provide object to convert' unless defined $obj;
547              
548 34         33 my $json;
549              
550 34 100       65 if ( ref $self ) {
551 22         21 my $method;
552 22 50       40 unless ( ref $self->[ENCODER] ) {
553 22 50       52 croak "No $handler Object created!"
554             unless exists $self->[HANDLER];
555 22         78 $method = $self->[HANDLER]->can( $self->[ENCODER] );
556 22 50       46 croak "$handler can't execute $self->[ENCODER]" unless $method;
557             }
558             else {
559 0         0 $method = $self->[ENCODER];
560             }
561 22         108 $json = $self->[HANDLER]->$method($obj);
562             }
563             else {
564 12         112 $json = $handler->can($encoder)->($obj);
565             }
566              
567 32 100 100     921 utf8::decode($json)
    100 66        
568             if ( ref $self ? $self->[UTF8] : $UTF8 )
569             and !utf8::is_utf8($json)
570             and utf8::valid($json);
571 32         109 return $json;
572             }
573              
574             #pod =over
575             #pod
576             #pod =item C
577             #pod
578             #pod =item C
579             #pod
580             #pod =item C
581             #pod
582             #pod Aliases for C, can be used interchangeably, regardless of the
583             #pod underlying JSON module.
584             #pod
585             #pod =back
586             #pod
587             #pod =cut
588              
589             *to_json = \&objToJson;
590             *Dump = \&objToJson;
591             *encode = \&objToJson;
592              
593             #pod =over
594             #pod
595             #pod =item C
596             #pod
597             #pod Takes a single argument, a string of JSON text to be converted
598             #pod back into a hashref.
599             #pod
600             #pod =back
601             #pod
602             #pod =cut
603              
604             sub jsonToObj {
605 16     16 1 4484 my $self = shift;
606 16         22 my $obj = shift;
607 16 50       34 croak 'must provide json to convert' unless defined $obj;
608              
609             # some handlers can't parse single booleans (I'm looking at you DWIW)
610 16 100       69 if ( $obj =~ /^(true|false)$/ ) {
611 2         8 return $self->$1;
612             }
613              
614 14 100       30 if ( ref $self ) {
615 9         10 my $method;
616 9 50       17 unless ( ref $self->[DECODER] ) {
617 9 50       18 croak "No $handler Object created!"
618             unless exists $self->[HANDLER];
619 9         36 $method = $self->[HANDLER]->can( $self->[DECODER] );
620 9 50       18 croak "$handler can't execute $self->[DECODER]" unless $method;
621             }
622             else {
623 0         0 $method = $self->[DECODER];
624             }
625 9         128 return $self->[HANDLER]->$method($obj);
626             }
627 5         27 $handler->can($decoder)->($obj);
628             }
629              
630             #pod =over
631             #pod
632             #pod =item C
633             #pod
634             #pod =item C
635             #pod
636             #pod =item C
637             #pod
638             #pod Aliases for C, can be used interchangeably, regardless of the
639             #pod underlying JSON module.
640             #pod
641             #pod =back
642             #pod
643             #pod =cut
644              
645             *from_json = \&jsonToObj;
646             *Load = \&jsonToObj;
647             *decode = \&jsonToObj;
648              
649             1;
650              
651             __END__