File Coverage

blib/lib/ETL/Yertl/Format/json.pm
Criterion Covered Total %
statement 27 27 100.0
branch 5 6 83.3
condition n/a
subroutine 8 8 100.0
pod 4 4 100.0
total 44 45 97.7


line stmt bran cond sub pod time code
1             package ETL::Yertl::Format::json;
2             our $VERSION = '0.037';
3             # ABSTRACT: JSON read/write support for Yertl
4              
5 4     4   87408 use ETL::Yertl;
  4         9  
  4         22  
6 4     4   120 use base 'ETL::Yertl::Format';
  4         6  
  4         540  
7 4     4   23 use Module::Runtime qw( use_module );
  4         8  
  4         18  
8 4     4   386 use ETL::Yertl::Util qw( pairs );
  4         7  
  4         2963  
9              
10             #pod =attr format_module
11             #pod
12             #pod The module being used for this format. Possible modules, in order of importance:
13             #pod
14             #pod =over 4
15             #pod
16             #pod =item L (any version)
17             #pod
18             #pod =item L (any version)
19             #pod
20             #pod =back
21             #pod
22             #pod =cut
23              
24             # Pairs of module => supported version
25             our @FORMAT_MODULES = (
26             'JSON::XS' => 0,
27             'JSON::PP' => 0,
28             );
29              
30             sub format_module {
31 17     17 1 36 my ( $self ) = @_;
32 17 50       68 return $self->{_format_module} if $self->{_format_module};
33 17         53 for my $format_module ( pairs @FORMAT_MODULES ) {
34 19         27 eval {
35             # Prototypes on use_module() make @$format_module not work correctly
36 19         69 use_module( $format_module->[0], $format_module->[1] );
37             };
38 19 100       6617 if ( !$@ ) {
39 16         82 return $format_module->[0];
40             }
41             }
42             die "Could not load a formatter for JSON. Please install one of the following modules:\n"
43             . join( "",
44 1 100       5 map { sprintf "\t%s (%s)", $_->[0], $_->[1] ? "version $_->[1]" : "Any version" }
  3         21  
45             pairs @FORMAT_MODULES
46             )
47             . "\n";
48             }
49              
50              
51             # Hash of MODULE => formatter sub
52             my %FORMAT_SUB = (
53              
54             'JSON::XS' => {
55             decode => sub {
56             my ( $self, $msg ) = @_;
57             state $json = JSON::XS->new->relaxed;
58             return $json->decode( $msg );
59             },
60             write => sub {
61             my $self = shift;
62             state $json = JSON::XS->new->canonical->pretty->allow_nonref;
63             return join( "", map { $json->encode( $_ ) } @_ );
64             },
65             read => sub {
66             my $self = shift;
67             state $json = JSON::XS->new->relaxed;
68             return $json->incr_parse( do { local $/; readline $self->{input} } );
69             },
70             },
71              
72             'JSON::PP' => {
73             decode => sub {
74             my ( $self, $msg ) = @_;
75             state $json = JSON::PP->new->relaxed;
76             return $json->decode( $msg );
77             },
78             write => sub {
79             my $self = shift;
80             state $json = JSON::PP->new->canonical->pretty->indent_length(3)->allow_nonref;
81             return join "", map { $json->encode( $_ ) } @_;
82             },
83             read => sub {
84             my $self = shift;
85             state $json = JSON::PP->new->relaxed;
86             require Storable;
87             local $Storable::canonical = 1;
88              
89             # Work around a bug in JSON::PP.
90             # incr_parse() only returns the first item, see: https://github.com/makamaka/JSON-PP/pull/7
91             my $text = do { local $/; readline $self->{input} };
92             my @objs = $json->incr_parse( $text );
93             if ( scalar @objs == 1 ) {
94             my @more_objs = $json->incr_parse( $text );
95             while ( Storable::freeze( $objs[0] ) ne Storable::freeze( $more_objs[0] ) ) {
96             push @objs, @more_objs;
97             @more_objs = $json->incr_parse( $text );
98             last if !@more_objs;
99             }
100             }
101              
102             return @objs;
103             },
104             },
105              
106             );
107              
108             #pod =method write( DOCUMENTS )
109             #pod
110             #pod Convert the given C to JSON. Returns a JSON string.
111             #pod
112             #pod =cut
113              
114             sub write {
115 7     7 1 40 my $self = shift;
116 7         27 return $FORMAT_SUB{ $self->format_module }{write}->( $self, @_ );
117             }
118              
119             #pod =method read()
120             #pod
121             #pod Read a JSON string from L and return all the documents
122             #pod
123             #pod =cut
124              
125             sub read {
126 7     7 1 35 my $self = shift;
127 7         30 return $FORMAT_SUB{ $self->format_module }{read}->( $self );
128             }
129              
130             #pod =method decode
131             #pod
132             #pod my $msg = $fmt->decode( $bytes );
133             #pod
134             #pod Decode the given bytes into the given message. The bytes must contain
135             #pod exactly one message to be decoded.
136             #pod
137             #pod =cut
138              
139             sub decode {
140 2     2 1 14 my ( $self, $bytes ) = @_;
141 2         8 return $FORMAT_SUB{ $self->format_module }{decode}->( $self, $bytes );
142             }
143              
144             1;
145              
146             __END__