File Coverage

blib/lib/JSON/Streaming/Writer.pm
Criterion Covered Total %
statement 154 173 89.0
branch 56 80 70.0
condition 16 24 66.6
subroutine 38 41 92.6
pod 12 17 70.5
total 276 335 82.3


line stmt bran cond sub pod time code
1              
2             package JSON::Streaming::Writer;
3              
4 3     3   27523 use strict;
  3         5  
  3         101  
5 3     3   15 use warnings;
  3         4  
  3         68  
6 3     3   2555 use IO::File;
  3         36631  
  3         2664  
7 3     3   26 use Carp;
  3         5  
  3         161  
8 3     3   15 use B;
  3         5  
  3         107  
9              
10 3     3   14 use constant ROOT_STATE => {};
  3         5  
  3         7437  
11              
12             our $VERSION = '0.03';
13              
14             sub for_stream {
15 34     34 0 50 my ($class, $fh) = @_;
16              
17 34         83 my $self = bless {}, $class;
18              
19 34         79 $self->{fh} = $fh;
20 34         54 $self->{state} = ROOT_STATE;
21 34         59 $self->{state_stack} = [];
22 34         67 $self->{used} = 0;
23 34         49 $self->{pretty} = 0;
24 34         47 $self->{indent_level} = 0;
25              
26 34         83 return $self;
27             }
28              
29             sub for_file {
30 0     0 0 0 my ($class, $filename) = @_;
31              
32 0         0 my $fh = IO::File->new($filename, O_WRONLY);
33 0         0 return $class->for_stream($fh);
34             }
35              
36             sub for_stdout {
37 0     0 0 0 my ($class, $filename) = @_;
38              
39 0         0 return $class->for_stream(\*STDOUT);
40             }
41              
42             sub pretty_output {
43 102     102 0 112 my $self = shift;
44              
45 102 50       200 if (@_) {
46 0 0       0 $self->{pretty} = $_[0] ? 1 : 0;
47             }
48              
49 102         308 $self->{pretty};
50             }
51              
52             sub start_object {
53 10     10 1 34 my ($self) = @_;
54              
55 10 50       24 Carp::croak("Can't start_object here") unless $self->_can_start_value;
56              
57 10         26 $self->_make_separator();
58 10         23 $self->_print("{");
59 10         29 my $state = $self->_push_state();
60 9         25 $state->{in_object} = 1;
61 9         22 $self->_indent();
62 9         18 return undef;
63             }
64              
65             sub end_object {
66 7     7 1 21 my ($self) = @_;
67              
68 7 50       17 Carp::croak("Can't end_object here: not in an object") unless $self->_in_object;
69 7         18 $self->_outdent();
70 7         16 $self->_make_end_block();
71 7         18 $self->_pop_state();
72 7         66 $self->_print("}");
73 7 50 33     19 $self->_print("\n") if $self->_state == ROOT_STATE && $self->pretty_output;
74              
75 7 50       18 $self->_state->{made_value} = 1 unless $self->_state == ROOT_STATE;
76             }
77              
78             sub start_property {
79 9     9 1 23 my ($self, $name) = @_;
80              
81 9 100       23 Carp::croak("Can't start_property here") unless $self->_can_start_property;
82              
83 5         13 $self->_make_separator();
84 5         13 my $state = $self->_push_state();
85 5         12 $state->{in_property} = 1;
86 5         13 $self->_print($self->_json_string($name), ":");
87             }
88              
89             sub end_property {
90 5     5 1 14 my ($self) = @_;
91              
92 5 50       13 Carp::croak("Can't end_property here: not in a property") unless $self->_in_property;
93 5 50       10 Carp::croak("Can't end_property here: haven't generated a value") unless $self->_made_value;
94              
95 5         11 $self->_pop_state();
96 5         17 $self->_state->{made_value} = 1;
97              
98             # end_property requires no output
99             }
100              
101             sub start_array {
102 17     17 1 48 my ($self) = @_;
103              
104 17 50       38 Carp::croak("Can't start_array here") unless $self->_can_start_value;
105              
106 17         45 $self->_make_separator();
107 17         41 $self->_print("[");
108 17         45 my $state = $self->_push_state();
109 17         39 $self->_indent();
110 17         25 $state->{in_array} = 1;
111 17         26 return undef;
112             }
113              
114             sub end_array {
115 15     15 1 37 my ($self) = @_;
116              
117 15 50       33 Carp::croak("Can't end_array here: not in an array") unless $self->_in_array;
118 15         40 $self->_outdent();
119 15         25 $self->_make_end_block();
120 15         32 $self->_pop_state();
121 15         42 $self->_print("]");
122 15 50 33     32 $self->_print("\n") if $self->_state == ROOT_STATE && $self->pretty_output;
123              
124 15 50       33 $self->_state->{made_value} = 1 unless $self->_state == ROOT_STATE;
125             }
126              
127             sub add_string {
128 9     9 1 23 my ($self, $value) = @_;
129              
130 9 100       22 Carp::croak("Can't add_string here") unless $self->_can_start_simple_value;
131              
132 5         13 $self->_make_separator();
133 5         15 $self->_print($self->_json_string($value));
134 5         13 $self->_state->{made_value} = 1;
135             }
136              
137             sub add_number {
138 7     7 1 19 my ($self, $value) = @_;
139              
140 7 100       14 Carp::croak("Can't add_number here") unless $self->_can_start_simple_value;
141              
142 5         15 $self->_make_separator();
143 5         66 $self->_print($value+0);
144 5         14 $self->_state->{made_value} = 1;
145             }
146              
147             sub add_boolean {
148 7     7 1 17 my ($self, $value) = @_;
149              
150 7 100       16 Carp::croak("Can't add_boolean here") unless $self->_can_start_simple_value;
151              
152 6         19 $self->_make_separator();
153 6 100       21 $self->_print($value ? 'true' : 'false');
154 6         15 $self->_state->{made_value} = 1;
155             }
156              
157             sub add_null {
158 11     11 1 26 my ($self) = @_;
159              
160 11 100       27 Carp::croak("Can't add_null here") unless $self->_can_start_simple_value;
161              
162 10         27 $self->_make_separator();
163 10         21 $self->_print('null');
164 10         24 $self->_state->{made_value} = 1;
165             }
166              
167             sub add_value {
168 32     32 1 113 my ($self, $value) = @_;
169              
170 32         47 my $type = ref($value);
171              
172 32 100       114 if (! defined($value)) {
    100          
    100          
    100          
    50          
173 6         14 $self->add_null();
174             }
175             elsif (! $type) {
176 11         63 my $b_obj = B::svref_2object(\$value);
177 11         53 my $flags = $b_obj->FLAGS;
178              
179 11 100 66     131 if (($flags & B::SVf_IOK or $flags & B::SVp_IOK or $flags & B::SVf_NOK or $flags & B::SVp_NOK) and !($flags & B::SVf_POK )) {
      66        
180 5         13 $self->add_number($value);
181             }
182             else {
183 6         17 $self->add_string($value);
184             }
185             }
186             elsif ($type eq 'ARRAY') {
187 8         18 $self->start_array();
188 8         18 foreach my $item (@$value) {
189 11         26 $self->add_value($item);
190             }
191 8         21 $self->end_array();
192             }
193             elsif ($type eq 'HASH') {
194 2         6 $self->start_object();
195 2         10 foreach my $k (sort keys %$value) {
196 1         6 $self->add_property($k, $value->{$k});
197             }
198 2         11 $self->end_object();
199             }
200             elsif ($type eq 'SCALAR') {
201 5 100       20 if ($$value eq '1') {
    50          
202 3         13 $self->add_boolean(1);
203             }
204             elsif ($$value eq '0') {
205 2         5 $self->add_boolean(0);
206             }
207             else {
208 0         0 Carp::croak("Don't know what to generate for $value");
209             }
210             }
211             else {
212 0         0 Carp::croak("Don't know what to generate for $value");
213             }
214             }
215              
216             sub add_property {
217 5     5 1 21 my ($self, $key, $value) = @_;
218              
219 5         14 $self->start_property($key);
220 3         10 $self->add_value($value);
221 3         9 $self->end_property();
222             }
223              
224             sub intentionally_ending_early {
225 5     5 0 25 my ($self) = @_;
226 5         12 $self->{intentionally_ending_early} = 1;
227             }
228              
229             sub _print {
230 89     89   168 my ($self, @data) = @_;
231              
232 89         385 $self->{fh}->print(join('', @data));
233             }
234              
235             sub _push_state {
236 32     32   43 my ($self) = @_;
237              
238 32 100 100     61 Carp::croak("Can't add anything else: JSON output is complete") if $self->_state == ROOT_STATE && $self->{used};
239              
240 31         86 $self->{used} = 1;
241              
242 31         32 push @{$self->{state_stack}}, $self->{state};
  31         74  
243              
244 31         128 $self->{state} = {
245             in_object => 0,
246             in_array => 0,
247             in_property => 0,
248             made_value => 0,
249             };
250              
251 31         67 return $self->{state};
252             }
253              
254             sub _pop_state {
255 27     27   36 my ($self) = @_;
256              
257 27         25 my $state = pop @{$self->{state_stack}};
  27         54  
258 27         52 return $self->{state} = $state;
259             }
260              
261             sub _state {
262 399     399   462 my ($self) = @_;
263              
264 399         3745 return $self->{state};
265             }
266              
267             sub _in_object {
268 77 100   77   144 return $_[0]->_state->{in_object} ? 1 : 0;
269             }
270              
271             sub _in_array {
272 15 50   15   33 return $_[0]->_state->{in_array} ? 1 : 0;
273             }
274              
275             sub _in_property {
276 66 100   66   115 return $_[0]->_state->{in_property} ? 1 : 0;
277             }
278              
279             sub _made_value {
280 68 100   68   108 return $_[0]->_state->{made_value} ? 1 : 0;
281             }
282              
283             sub _can_start_value {
284              
285 61 50 66 61   130 return 0 if $_[0]->_in_property && $_[0]->_made_value;
286              
287 61 100       143 return $_[0]->_in_object ? 0 : 1;
288             }
289              
290             sub _can_start_simple_value {
291             # Can't generate simple values in the root state
292 34   100 34   71 return $_[0]->_can_start_value && $_[0]->_state != ROOT_STATE;
293             }
294              
295             sub _can_start_property {
296 9 100   9   21 return $_[0]->_in_object ? 1 : 0;
297             }
298              
299             sub _make_separator {
300 58 100   58   114 $_[0]->_print(",") if $_[0]->_made_value;
301 58 50       130 if ($_[0]->pretty_output) {
302 0 0       0 if ($_[0]->_in_property) {
303 0         0 $_[0]->_print(" ");
304             }
305             else {
306 0         0 $_[0]->_print("\n");
307 0         0 $_[0]->_make_indent();
308             }
309             }
310             }
311              
312             sub _make_end_block {
313 22 50   22   49 return unless $_[0]->pretty_output;
314              
315 0 0       0 if ($_[0]->_made_value) {
316 0         0 $_[0]->_print("\n");
317 0         0 $_[0]->_make_indent();
318             }
319             }
320              
321             sub _make_indent {
322 0     0   0 $_[0]->_print(" " x $_[0]->{indent_level});
323             }
324              
325             sub _indent {
326 26     26   49 $_[0]->{indent_level}++;
327             }
328              
329             sub _outdent {
330 22     22   43 $_[0]->{indent_level}--;
331             }
332              
333             my %esc = (
334             "\n" => '\n',
335             "\r" => '\r',
336             "\t" => '\t',
337             "\f" => '\f',
338             "\b" => '\b',
339             "\"" => '\"',
340             "\\" => '\\\\',
341             "\'" => '\\\'',
342             );
343             sub _json_string {
344 10     10   19 my ($class, $value) = @_;
345              
346 10         20 $value =~ s/([\x22\x5c\n\r\t\f\b])/$esc{$1}/eg;
  0         0  
347 10         22 $value =~ s/\//\\\//g;
348 10         14 $value =~ s/([\x00-\x08\x0b\x0e-\x1f])/'\\u00' . unpack('H2', $1)/eg;
  0         0  
349              
350 10         37 return '"'.$value.'"';
351             }
352              
353             sub DESTROY {
354 34     34   10717 my ($self) = @_;
355              
356 34 50 66     79 if ($self->_state != ROOT_STATE && ! $self->{intentionally_ending_early}) {
357 0           warn "JSON::Streaming::Writer object was destroyed with incomplete output";
358             }
359             }
360              
361             1;
362              
363             =head1 NAME
364              
365             JSON::Streaming::Writer - Generate JSON output in a streaming manner
366              
367             =head1 SYNOPSIS
368              
369             my $jsonw = JSON::Streaming::Writer->for_stream($fh)
370             $jsonw->start_object();
371             $jsonw->add_simple_property("someName" => "someValue");
372             $jsonw->add_simple_property("someNumber" => 5);
373             $jsonw->start_property("someObject");
374             $jsonw->start_object();
375             $jsonw->add_simple_property("someOtherName" => "someOtherValue");
376             $jsonw->add_simple_property("someOtherNumber" => 6);
377             $jsonw->end_object();
378             $jsonw->end_property();
379             $jsonw->start_property("someArray");
380             $jsonw->start_array();
381             $jsonw->add_simple_item("anotherStringValue");
382             $jsonw->add_simple_item(10);
383             $jsonw->start_object();
384             # No items; this object is empty
385             $jsonw->end_object();
386             $jsonw->end_array();
387              
388             =head1 DESCRIPTION
389              
390             Most JSON libraries work in terms of in-memory data structures. In Perl,
391             JSON serializers often expect to be provided with a HASH or ARRAY ref
392             containing all of the data you want to serialize.
393              
394             This library allows you to generate syntactically-correct JSON without
395             first assembling your complete data structure in memory. This allows
396             large structures to be returned without requiring those
397             structures to be memory-resident, and also allows parts of the output
398             to be made available to a streaming-capable JSON parser while
399             the rest of the output is being generated, which may improve
400             performance of JSON-based network protocols.
401              
402             =head1 RAW API
403              
404             The raw API allows the caller precise control over the generated
405             data structure by providing explicit methods for each fundamental JSON
406             construct.
407              
408             As a general rule, methods with names starting with C and C
409             methods wrap a multi-step construct and must be used symmetrically, while
410             methods with names starting with C stand alone and generate output
411             in a single step.
412              
413             The raw API methods are described below
414              
415             =head2 start_object, end_object
416              
417             These methods delimit a JSON object. C can be called
418             as the first method call on a writer object to produce a top-level
419             object, or it can be called in any state where a value is expected
420             to produce a nested object.
421              
422             JSON objects contain properties, so only property-related methods
423             may be used while in the context of an object.
424              
425             =head2 start_array, end_array
426              
427             These methods delimit a JSON array. C can be called
428             as the first method call on a writer object to produce a top-level
429             array, or it can be called in any state where a value is expected
430             to produce a nested array.
431              
432             JSON arrays contain properties, so only value-producing methods
433             may be used while in the context of an array.
434              
435             =head2 start_property($name), end_property
436              
437             These methods delimit a property or member of a JSON object.
438             C may be called only when in the context of an
439             object. The C<$name> parameter, a string, gives the name that
440             the generated property will have.
441              
442             Only value-producing methods may be used while in the context
443             of a property.
444              
445             Since a property can contain only one value, only a single
446             value-producing method may be called between a pair of
447             C and C calls.
448              
449             =head2 add_string($value)
450              
451             Produces a JSON string with the given value.
452              
453             =head2 add_number($value)
454              
455             Produces a JSON number whose value is Perl's numeric interpretation of the given value.
456              
457             =head2 add_boolean($value)
458              
459             Produces a JSON boolean whose value is Perl's boolean interpretation of the given value.
460              
461             =head2 add_null
462              
463             Produces a JSON C.
464              
465             =head1 DWIM API
466              
467             The DWIM API allows you to provide normal Perl data structures and have the library
468             figure out a sensible JSON representation for them. You can mix use of the raw
469             and DWIM APIs to allow you to exercise fine control where required but use
470             a simpler API for normal cases.
471              
472             =head2 add_value($value)
473              
474             Produces a JSON value representing the given Perl value. This library can handle
475             Perl strings, integers (i.e. scalars that have most recently been used as numbers),
476             references to the values 0 and 1 representing booleans and C representing
477             a JSON C. It can also accept ARRAY and HASH refs that contain such values
478             and produce JSON array and object values recursively, much like a non-streaming
479             JSON producer library would do.
480              
481             This method is a wrapper around the corresponding raw API calls, so the error
482             messages it generates will often refer to the underlying raw API.
483              
484             =head2 add_property($name, $value)
485              
486             Produces a property inside a JSON object whose value is derived from the provided
487             value using the same mappings as used by C. This can only be used
488             inside the context of an object, and is really just a wrapper around a C,
489             C, C sequence for convenience.
490              
491             =head1 OPTIONS
492              
493             =head2 Pretty Output
494              
495             This library can optionally pretty-print the JSON string it produces. To enable this,
496             call the C method with a true value as its first argument.
497              
498             You can enable and disable pretty-printing during output, though if you do the
499             results are likely to be sub-optimal as the additional whitespace may not be
500             generated where you'd expect. In particular, where the whitespace is generated
501             may change in future versions.
502              
503             =head1 INTERNALS
504              
505             Internally this library maintains a simple state stack that allows
506             it to remember where it is without needing to remember the data
507             it has already generated.
508              
509             The state stack means that it will use more memory for deeper
510             data structures.
511              
512             =head1 LICENSE
513              
514             Copyright 2009 Martin Atkins .
515              
516             This program is free software; you can redistribute it and/or modify it under
517             the same terms as Perl itself.