File Coverage

blib/lib/Mojo/JSON.pm
Criterion Covered Total %
statement 138 139 99.2
branch 89 90 98.8
condition 14 14 100.0
subroutine 32 32 100.0
pod 5 5 100.0
total 278 280 99.2


line stmt bran cond sub pod time code
1             package Mojo::JSON;
2 75     75   160725 use Mojo::Base -strict;
  75         109  
  75         400  
3              
4 75     75   360 use Carp qw(croak);
  75         130  
  75         3617  
5 75     75   318 use Exporter qw(import);
  75         137  
  75         1840  
6 75     75   50219 use JSON::PP ();
  75         881777  
  75         2533  
7 75     75   1845 use Mojo::Util qw(decode encode monkey_patch);
  75         136  
  75         4062  
8 75     75   419 use overload ();
  75         103  
  75         1220  
9 75     75   238 use Scalar::Util qw(blessed);
  75         92  
  75         6472  
10              
11             # For better performance Cpanel::JSON::XS is required
12             use constant JSON_XS => $ENV{MOJO_NO_JSON_XS}
13             ? 0
14 75 100   75   330 : !!eval { require Cpanel::JSON::XS; Cpanel::JSON::XS->VERSION('4.20'); 1 };
  75         94  
  75         345  
  74         63825  
  74         256017  
  74         5313  
15              
16 75     75   482 use constant CORE_BOOLS => defined &builtin::is_bool;
  75         111  
  75         3049  
17              
18             # Maximum nesting level for decoding, to match the default of Cpanel::JSON::XS
19 75     75   265 use constant MAX_DEPTH => 512;
  75         105  
  75         4233  
20              
21             BEGIN {
22 75     75   2972 warnings->unimport('experimental::builtin') if CORE_BOOLS;
23             }
24              
25             # Deep recursion is expected when working with nested data structures
26 75     75   309 no warnings 'recursion';
  75         105  
  75         179155  
27              
28             our @EXPORT_OK = qw(decode_json encode_json false from_json j to_json true);
29              
30             # Current nesting level while decoding
31             our $DEPTH = 0;
32              
33             # Escaped special character map
34             my %ESCAPE
35             = ('"' => '"', '\\' => '\\', '/' => '/', 'b' => "\x08", 'f' => "\x0c", 'n' => "\x0a", 'r' => "\x0d", 't' => "\x09");
36             my %REVERSE = map { $ESCAPE{$_} => "\\$_" } keys %ESCAPE;
37             for (0x00 .. 0x1f) { $REVERSE{pack 'C', $_} //= sprintf '\u%.4X', $_ }
38              
39             # Replace pure-Perl fallbacks if Cpanel::JSON::XS is available
40             if (JSON_XS) {
41             my $BINARY = Cpanel::JSON::XS->new->utf8;
42             my $TEXT = Cpanel::JSON::XS->new;
43             $_->canonical->allow_nonref->allow_unknown->allow_blessed->convert_blessed->stringify_infnan->escape_slash
44             ->allow_dupkeys
45             for $BINARY, $TEXT;
46 147     147   15912 monkey_patch __PACKAGE__, 'encode_json', sub { $BINARY->encode($_[0]) };
47 137     137   7583 monkey_patch __PACKAGE__, 'decode_json', sub { $BINARY->decode($_[0]) };
48 2     2   647 monkey_patch __PACKAGE__, 'to_json', sub { $TEXT->encode($_[0]) };
49 10     10   987 monkey_patch __PACKAGE__, 'from_json', sub { $TEXT->decode($_[0]) };
50             }
51              
52             sub decode_json {
53 74     74 1 88224 my $err = _decode(\my $value, shift);
54 74 100       3017 return defined $err ? croak $err : $value;
55             }
56              
57 60     60 1 83299 sub encode_json { encode('UTF-8', _encode_value(shift)) }
58              
59 16     16 1 3021 sub false () {JSON::PP::false}
60              
61             sub from_json {
62             my $err = _decode(\my $value, shift, 1);
63             return defined $err ? croak $err : $value;
64             }
65              
66             sub j {
67 99 100 100 99 1 16093 return encode_json($_[0]) if ref $_[0] eq 'ARRAY' || ref $_[0] eq 'HASH';
68 95         283 return scalar eval { decode_json($_[0]) };
  92         279  
69             }
70              
71             sub to_json { _encode_value(shift) }
72              
73 21     21 1 14124 sub true () {JSON::PP::true}
74              
75             sub _decode {
76 77     77   233 my $valueref = shift;
77              
78 77 100       127 eval {
79              
80             # Missing input
81 77 100       277 die "Missing or empty input at offset 0\n" unless length(local $_ = shift);
82              
83             # UTF-8
84 76 100       316 $_ = decode('UTF-8', $_) unless shift;
85 76 100       191 die "Input is not UTF-8 encoded\n" unless defined;
86              
87             # Value
88 74         156 $$valueref = _decode_value();
89              
90             # Leftover data
91 58 100       392 /\G[\x20\x09\x0a\x0d]*\z/gc or _throw('Unexpected data');
92             } ? return undef : chomp $@;
93              
94 24         63 return $@;
95             }
96              
97             sub _decode_array {
98              
99             # Nesting limit
100 1847 100   1847   3029 _throw('Nesting too deep') if (local $DEPTH = $DEPTH + 1) > MAX_DEPTH;
101              
102 1845         2007 my @array;
103 1845         4211 until (m/\G[\x20\x09\x0a\x0d]*\]/gc) {
104              
105             # Value
106 1865         3019 push @array, _decode_value();
107              
108             # Separator
109 1092 100       1832 redo if /\G[\x20\x09\x0a\x0d]*,/gc;
110              
111             # End
112 1066 100       2049 last if /\G[\x20\x09\x0a\x0d]*\]/gc;
113              
114             # Invalid character
115 2         5 _throw('Expected comma or right square bracket while parsing array');
116             }
117              
118 1070         1872 return \@array;
119             }
120              
121             sub _decode_object {
122              
123             # Nesting limit
124 1297 100   1297   2817 _throw('Nesting too deep') if (local $DEPTH = $DEPTH + 1) > MAX_DEPTH;
125              
126 1296         1837 my %hash;
127 1296         5216 until (m/\G[\x20\x09\x0a\x0d]*\}/gc) {
128              
129             # Quote
130 1300 100       3198 /\G[\x20\x09\x0a\x0d]*"/gc or _throw('Expected string while parsing object');
131              
132             # Key
133 1297         2290 my $key = _decode_string();
134              
135             # Colon
136 1297 100       3424 /\G[\x20\x09\x0a\x0d]*:/gc or _throw('Expected colon while parsing object');
137              
138             # Value
139 1296         2472 $hash{$key} = _decode_value();
140              
141             # Separator
142 528 100       1043 redo if /\G[\x20\x09\x0a\x0d]*,/gc;
143              
144             # End
145 522 100       1212 last if /\G[\x20\x09\x0a\x0d]*\}/gc;
146              
147             # Invalid character
148 1         2 _throw('Expected comma or right curly bracket while parsing object');
149             }
150              
151 523         1376 return \%hash;
152             }
153              
154             sub _decode_string {
155 1344     1344   2358 my $pos = pos;
156              
157             # Extract string with escaped characters
158 1344         19675 m!\G((?:(?:[^\x00-\x1f\\"]|\\(?:["\\/bfnrt]|u[0-9a-fA-F]{4})){0,32766})*)!gc;
159 1344         2386 my $str = $1;
160              
161             # Invalid character
162 1344 100       3079 unless (m/\G"/gc) {
163 2 100       11 _throw('Unexpected character or invalid escape while parsing string') if /\G[\x00-\x1f\\]/;
164 1         3 _throw('Unterminated string');
165             }
166              
167             # Unescape popular characters
168 1342 100       3541 if (index($str, '\\u') < 0) {
169 1337         1985 $str =~ s!\\(["\\/bfnrt])!$ESCAPE{$1}!gs;
170 1337         2917 return $str;
171             }
172              
173             # Unescape everything else
174 5         10 my $buffer = '';
175 5         31 while ($str =~ /\G([^\\]*)\\(?:([^u])|u(.{4}))/gc) {
176 10         24 $buffer .= $1;
177              
178             # Popular character
179 10 100       23 if ($2) { $buffer .= $ESCAPE{$2} }
  4         18  
180              
181             # Escaped
182             else {
183 6         22 my $ord = hex $3;
184              
185             # Surrogate pair
186 6 100       20 if (($ord & 0xf800) == 0xd800) {
187              
188             # High surrogate
189 3 100       14 ($ord & 0xfc00) == 0xd800 or pos = $pos + pos($str), _throw('Missing high-surrogate');
190              
191             # Low surrogate
192 2 100       12 $str =~ /\G\\u([Dd][C-Fc-f]..)/gc or pos = $pos + pos($str), _throw('Missing low-surrogate');
193              
194 1         4 $ord = 0x10000 + ($ord - 0xd800) * 0x400 + (hex($1) - 0xdc00);
195             }
196              
197             # Character
198 4         61 $buffer .= pack 'U', $ord;
199             }
200             }
201              
202             # The rest
203 3         18 return $buffer . substr $str, pos($str), length($str);
204             }
205              
206             sub _decode_value {
207              
208             # Leading whitespace
209 3235     3235   5378 /\G[\x20\x09\x0a\x0d]*/gc;
210              
211             # String
212 3235 100       5666 return _decode_string() if /\G"/gc;
213              
214             # Object
215 3188 100       6878 return _decode_object() if /\G\{/gc;
216              
217             # Array
218 1891 100       4667 return _decode_array() if /\G\[/gc;
219              
220             # Number
221 44 100       276 return 0 + $1 if /\G([-]?(?:0|[1-9][0-9]*)(?:\.[0-9]*)?(?:[eE][+-]?[0-9]+)?)/gc;
222              
223             # True
224 16 100       46 return true() if /\Gtrue/gc;
225              
226             # False
227 11 100       45 return false() if /\Gfalse/gc;
228              
229             # Null
230 7 100       34 return undef if /\Gnull/gc;
231              
232             # Invalid character
233 2         7 _throw('Expected string, array, object, number, boolean or null');
234             }
235              
236             sub _encode_array {
237 52     52   83 '[' . join(',', map { _encode_value($_) } @{$_[0]}) . ']';
  74         175  
  52         175  
238             }
239              
240             sub _encode_object {
241 19     19   38 my $object = shift;
242 19         67 my @pairs = map { _encode_string($_) . ':' . _encode_value($object->{$_}) } sort keys %$object;
  17         43  
243 19         117 return '{' . join(',', @pairs) . '}';
244             }
245              
246             sub _encode_string {
247 57     57   176 my $str = shift;
248 57         309 $str =~ s!([\x00-\x1f\\"/])!$REVERSE{$1}!gs;
249 57         480 return "\"$str\"";
250             }
251              
252             sub _encode_value {
253 155     155   277 my $value = shift;
254              
255             # Reference
256 155 100       453 if (my $ref = ref $value) {
257              
258             # Object
259 88 100       225 return _encode_object($value) if $ref eq 'HASH';
260              
261             # Array
262 69 100       179 return _encode_array($value) if $ref eq 'ARRAY';
263              
264             # True or false
265 17 100       44 return $$value ? 'true' : 'false' if $ref eq 'SCALAR';
    100          
266 15 100       47 return $value ? 'true' : 'false' if $ref eq 'JSON::PP::Boolean';
    100          
267              
268             # Everything else
269 6 100       17 return 'null' unless blessed $value;
270 5 100       55 return overload::Method($value, '""') ? _encode_string($value) : 'null' unless my $sub = $value->can('TO_JSON');
    100          
271 2         6 return _encode_value($value->$sub);
272             }
273              
274             # Null
275 67 100       157 return 'null' unless defined $value;
276              
277             # Boolean
278 63 100       155 return $value ? 'true' : 'false' if CORE_BOOLS && builtin::is_bool($value);
    100          
279              
280             # Number
281 75     75   543 no warnings 'numeric';
  75         107  
  75         25980  
282 61 100 100     601 return $value
      100        
      100        
283             if !utf8::is_utf8($value) && length((my $dummy = '') & $value) && 0 + $value eq $value && $value * 0 == 0;
284              
285             # String
286 38         108 return _encode_string($value);
287             }
288              
289             sub _throw {
290              
291             # Leading whitespace
292 21     21   58 /\G[\x20\x09\x0a\x0d]*/gc;
293              
294             # Context
295 21         48 my $context = 'Malformed JSON: ' . shift;
296 21 50       72 if (m/\G\z/gc) { $context .= ' before end of data' }
  0         0  
297             else {
298 21         119 my @lines = split /\n/, substr($_, 0, pos);
299 21   100     114 $context .= ' at line ' . @lines . ', offset ' . length(pop @lines || '');
300             }
301              
302 21         865 die "$context\n";
303             }
304              
305             1;
306              
307             =encoding utf8
308              
309             =head1 NAME
310              
311             Mojo::JSON - Minimalistic JSON
312              
313             =head1 SYNOPSIS
314              
315             use Mojo::JSON qw(decode_json encode_json);
316              
317             my $bytes = encode_json {foo => [1, 2], bar => 'hello!', baz => \1};
318             my $hash = decode_json $bytes;
319              
320             =head1 DESCRIPTION
321              
322             L is a minimalistic and possibly the fastest pure-Perl implementation of L
323             8259|https://tools.ietf.org/html/rfc8259>.
324              
325             It supports normal Perl data types like scalar, array reference, hash reference and will try to call the C
326             method on blessed references, or stringify them if it doesn't exist. Differentiating between strings and numbers in
327             Perl is hard, depending on how it has been used, a scalar can be both at the same time. The string value has a higher
328             precedence unless both representations are equivalent.
329              
330             [1, -2, 3] -> [1, -2, 3]
331             {"foo": "bar"} -> {foo => 'bar'}
332              
333             Literal names will be translated to and from L constants or a similar native Perl value.
334              
335             true -> Mojo::JSON->true
336             false -> Mojo::JSON->false
337             null -> undef
338              
339             In addition scalar references will be used to generate booleans, based on if their values are true or false.
340              
341             \1 -> true
342             \0 -> false
343              
344             The character C will always be escaped to prevent XSS attacks.
345              
346             "" -> "<\/script>"
347              
348             For better performance the optional module L (4.20+) will be used automatically if possible. This can
349             also be disabled with the C environment variable.
350              
351             =head1 FUNCTIONS
352              
353             L implements the following functions, which can be imported individually.
354              
355             =head2 decode_json
356              
357             my $value = decode_json $bytes;
358              
359             Decode JSON to Perl value and die if decoding fails.
360              
361             =head2 encode_json
362              
363             my $bytes = encode_json {i => '♥ mojolicious'};
364              
365             Encode Perl value to JSON.
366              
367             =head2 false
368              
369             my $false = false;
370              
371             False value, used because Perl has no native equivalent.
372              
373             =head2 from_json
374              
375             my $value = from_json $chars;
376              
377             Decode JSON text that is not C encoded to Perl value and die if decoding fails.
378              
379             =head2 j
380              
381             my $bytes = j [1, 2, 3];
382             my $bytes = j {i => '♥ mojolicious'};
383             my $value = j $bytes;
384              
385             Encode Perl data structure (which may only be an array reference or hash reference) or decode JSON, an C return
386             value indicates a bare C or that decoding failed.
387              
388             =head2 to_json
389              
390             my $chars = to_json {i => '♥ mojolicious'};
391              
392             Encode Perl value to JSON text without C encoding it.
393              
394             =head2 true
395              
396             my $true = true;
397              
398             True value, used because Perl has no native equivalent.
399              
400             =head1 SEE ALSO
401              
402             L, L, L.
403              
404             =cut