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