File Coverage

blib/lib/Data/Clean/JSON.pm
Criterion Covered Total %
statement 33 34 97.0
branch n/a
condition 13 26 50.0
subroutine 8 9 88.8
pod 4 4 100.0
total 58 73 79.4


line stmt bran cond sub pod time code
1             package Data::Clean::JSON;
2              
3             our $DATE = '2019-09-11'; # DATE
4             our $VERSION = '0.392'; # VERSION
5              
6 1     1   101176 use 5.010001;
  1         13  
7 1     1   5 use strict;
  1         2  
  1         19  
8 1     1   5 use warnings;
  1         1  
  1         27  
9              
10 1     1   416 use parent qw(Data::Clean);
  1         288  
  1         8  
11              
12 1     1   4671 use Exporter qw(import);
  1         3  
  1         245  
13             our @EXPORT_OK = qw(
14             clean_json_in_place
15             clone_and_clean_json
16             );
17              
18             sub new {
19 2     2 1 3154 my ($class, %opts) = @_;
20              
21             # from FromJSON
22 2   50     18 $opts{"JSON::PP::Boolean"} //= ['one_or_zero'];
23 2   50     12 $opts{"JSON::XS::Boolean"} //= ['one_or_zero']; # this doesn't exist though
24 2   50     11 $opts{"Cpanel::JSON::XS::Boolean"} //= ['one_or_zero']; # this doesn't exist though
25              
26 2   50     12 $opts{DateTime} //= [call_method => 'epoch'];
27 2   50     12 $opts{'Time::Moment'} //= [call_method => 'epoch'];
28 2   50     11 $opts{'Math::BigInt'} //= [call_method => 'bstr'];
29 2   50     12 $opts{Regexp} //= ['stringify'];
30 2   50     10 $opts{version} //= ['stringify'];
31              
32 2   50     11 $opts{SCALAR} //= ['deref_scalar'];
33 2   50     10 $opts{-ref} //= ['replace_with_ref'];
34 2   50     10 $opts{-circular} //= ['clone'];
35 2   50     9 $opts{-obj} //= ['unbless'];
36              
37 2   50     9 $opts{'!recurse_obj'} //= 1;
38 2         18 $class->SUPER::new(%opts);
39             }
40              
41             sub get_cleanser {
42 2     2 1 142 my $class = shift;
43 2         8 state $singleton = $class->new;
44 2         3673 $singleton;
45             }
46              
47             sub clean_json_in_place {
48 1     1 1 13288 __PACKAGE__->get_cleanser->clean_in_place(@_);
49             }
50              
51             sub clone_and_clean_json {
52 0     0 1   __PACKAGE__->get_cleanser->clone_and_clean(@_);
53             }
54              
55             1;
56             # ABSTRACT: (DEPRECATED) Clean data so it is safe to output to JSON
57              
58             __END__
59              
60             =pod
61              
62             =encoding UTF-8
63              
64             =head1 NAME
65              
66             Data::Clean::JSON - (DEPRECATED) Clean data so it is safe to output to JSON
67              
68             =head1 VERSION
69              
70             This document describes version 0.392 of Data::Clean::JSON (from Perl distribution Data-Clean-JSON), released on 2019-09-11.
71              
72             =head1 SYNOPSIS
73              
74             use Data::Clean::JSON;
75             my $cleanser = Data::Clean::JSON->get_cleanser;
76             my $data = { code=>sub {}, re=>qr/abc/i };
77              
78             my $cleaned;
79              
80             # modifies data in-place
81             $cleaned = $cleanser->clean_in_place($data);
82              
83             # ditto, but deep clone first, return
84             $cleaned = $cleanser->clone_and_clean($data);
85              
86             # now output it
87             use JSON;
88             print encode_json($cleaned); # prints '{"code":"CODE","re":"(?^i:abc)"}'
89              
90             Functional shortcuts:
91              
92             use Data::Clean::JSON qw(clean_json_in_place clone_and_clean_json);
93              
94             # equivalent to Data::Clean::JSON->get_cleanser->clean_in_place($data)
95             clean_json_in_place($data);
96              
97             # equivalent to Data::Clean::JSON->get_cleanser->clone_and_clean($data)
98             $cleaned = clone_and_clean_json($data);
99              
100             =head1 DESCRIPTION
101              
102             B<DEPRECATION NOTICE:> This module has been renamed to L<Data::Clean::ForJSON>
103             for more clarity. This old name is retained because there are distributions
104             still depending on it.
105              
106             This class cleans data from anything that might be problematic when encoding to
107             JSON. This includes coderefs, globs, and so on. Here's what it will do by
108             default:
109              
110             =over
111              
112             =item * Change DateTime and Time::Moment object to its epoch value
113              
114             =item * Change Regexp and version object to its string value
115              
116             =item * Change scalar references (e.g. \1) to its scalar value (e.g. 1)
117              
118             =item * Change other references (non-hash, non-array) to its ref() value (e.g. "GLOB", "CODE")
119              
120             =item * Clone circular references
121              
122             With a default limit of 1, meaning that if a reference is first seen again for
123             the first time, it will be cloned. But if it is seen again for the second time,
124             it will be replaced with "CIRCULAR".
125              
126             To change the default limit, customize your cleanser object:
127              
128             $cleanser = Data::Clean::JSON->new(
129             -circular => ["clone", 4],
130             );
131              
132             or you can perform other action for circular references, see L<Data::Clean> for
133             more details.
134              
135             =item * Unbless other types of objects
136              
137             =back
138              
139             Cleaning recurses into objects.
140              
141             Data that has been cleaned will probably not be convertible back to the
142             original, due to information loss (for example, coderefs converted to string
143             C<"CODE">).
144              
145             The design goals are good performance, good defaults, and just enough
146             flexibility. The original use-case is for returning JSON response in HTTP API
147             service.
148              
149             This module is significantly faster than modules like L<Data::Rmap> or
150             L<Data::Visitor::Callback> because with something like Data::Rmap you repeatedly
151             invoke callback for each data item. This module, on the other hand, generates a
152             cleanser code using eval(), using native Perl for() loops.
153              
154             If C<LOG_CLEANSER_CODE> environment is set to true, the generated cleanser code
155             will be logged using L<Log::get> at trace level. You can see it, e.g. using
156             L<Log::ger::Output::Screen>:
157              
158             % LOG_CLEANSER_CODE=1 perl -MLog::ger::Output=Screen -MLog::ger::Level::trace -MData::Clean::JSON \
159             -e'$c=Data::Clean::JSON->new; ...'
160              
161             =head1 FUNCTIONS
162              
163             None of the functions are exported by default.
164              
165             =head2 clean_json_in_place($data)
166              
167             A shortcut for:
168              
169             Data::Clean::JSON->get_cleanser->clean_in_place($data)
170              
171             =head2 clone_and_clean_json($data) => $cleaned
172              
173             A shortcut for:
174              
175             $cleaned = Data::Clean::JSON->get_cleanser->clone_and_clean($data)
176              
177             =head1 METHODS
178              
179             =head2 CLASS->get_cleanser => $obj
180              
181             Return a singleton instance, with default options. Use C<new()> if you want to
182             customize options.
183              
184             =head2 CLASS->new() => $obj
185              
186             Create a new instance.
187              
188             =head2 $obj->clean_in_place($data) => $cleaned
189              
190             Clean $data. Modify data in-place.
191              
192             =head2 $obj->clone_and_clean($data) => $cleaned
193              
194             Clean $data. Clone $data first.
195              
196             =head1 FAQ
197              
198             =head2 Why clone/modify? Why not directly output JSON?
199              
200             So that the data can be used for other stuffs, like outputting to YAML, etc.
201              
202             =head2 Why is it slow?
203              
204             If you use C<new()> instead of C<get_cleanser()>, make sure that you do not
205             construct the Data::Clean::JSON object repeatedly, as the constructor generates
206             the cleanser code first using eval(). A short benchmark (run on my slow Atom
207             netbook):
208              
209             % bench -MData::Clean::JSON -b'$c=Data::Clean::JSON->new' \
210             'Data::Clean::JSON->new->clone_and_clean([1..100])' \
211             '$c->clone_and_clean([1..100])'
212             Benchmarking sub { Data::Clean::JSON->new->clean_in_place([1..100]) }, sub { $c->clean_in_place([1..100]) } ...
213             a: 302 calls (291.3/s), 1.037s (3.433ms/call)
214             b: 7043 calls (4996/s), 1.410s (0.200ms/call)
215             Fastest is b (17.15x a)
216              
217             Second, you can turn off some checks if you are sure you will not be getting bad
218             data. For example, if you know that your input will not contain circular
219             references, you can turn off circular detection:
220              
221             $cleanser = Data::Clean::JSON->new(-circular => 0);
222              
223             Benchmark:
224              
225             $ perl -MData::Clean::JSON -MBench -E '
226             $data = [[1],[2],[3],[4],[5]];
227             bench {
228             circ => sub { state $c = Data::Clean::JSON->new; $c->clone_and_clean($data) },
229             nocirc => sub { state $c = Data::Clean::JSON->new(-circular=>0); $c->clone_and_clean($data) }
230             }, -1'
231             circ: 9456 calls (9425/s), 1.003s (0.106ms/call)
232             nocirc: 13161 calls (12885/s), 1.021s (0.0776ms/call)
233             Fastest is nocirc (1.367x circ)
234              
235             The less number of checks you do, the faster the cleansing process will be.
236              
237             =head2 Why am I getting 'Not a CODE reference at lib/Data/Clean.pm line xxx'?
238              
239             [2013-08-07 ] This error message is from Data::Clone::clone() when it is cloning
240             an object. If you are cleaning objects, instead of using clone_and_clean(), try
241             using clean_in_place(). Or, clone your data first using something else like
242             L<Sereal>.
243              
244             =head1 ENVIRONMENT
245              
246             LOG_CLEANSER_CODE
247              
248             =head1 HOMEPAGE
249              
250             Please visit the project's homepage at L<https://metacpan.org/release/Data-Clean-JSON>.
251              
252             =head1 SOURCE
253              
254             Source repository is at L<https://github.com/perlancar/perl-Data-Clean-JSON>.
255              
256             =head1 BUGS
257              
258             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Data-Clean-JSON>
259              
260             When submitting a bug or request, please include a test-file or a
261             patch to an existing test-file that illustrates the bug or desired
262             feature.
263              
264             =head1 SEE ALSO
265              
266             L<Data::Rmap>
267              
268             L<Data::Visitor::Callback>
269              
270             L<Data::Abridge> is similar in goal, which is to let Perl data structures (which
271             might contain stuffs unsupported in JSON) be encodeable to JSON. But unlike
272             Data::Clean::JSON, it has some (currently) non-configurable rules, like changing
273             a coderef with a hash C<< {CODE=>'\&main::__ANON__'} >> or a scalar ref with C<<
274             {SCALAR=>'value'} >> and so on. Note that the abridging process is similarly
275             unidirectional (you cannot convert back the original Perl data structure).
276              
277             =head1 AUTHOR
278              
279             perlancar <perlancar@cpan.org>
280              
281             =head1 COPYRIGHT AND LICENSE
282              
283             This software is copyright (c) 2019, 2018, 2017, 2016, 2015, 2014, 2013, 2012 by perlancar@cpan.org.
284              
285             This is free software; you can redistribute it and/or modify it under
286             the same terms as the Perl 5 programming language system itself.
287              
288             =cut