File Coverage

blib/lib/XML/FeedPP/Plugin/DumpJSON.pm
Criterion Covered Total %
statement 68 117 58.1
branch 25 62 40.3
condition 8 15 53.3
subroutine 11 15 73.3
pod 0 9 0.0
total 112 218 51.3


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             XML::FeedPP::Plugin::DumpJSON - FeedPP Plugin for generating JSON
4              
5             =head1 SYNOPSIS
6              
7             use XML::FeedPP;
8             my $feed = XML::FeedPP->new( 'index.rss' );
9             $feed->limit_item( 10 );
10             $feed->call( DumpJSON => 'index-rss.json' );
11              
12             =head1 DESCRIPTION
13              
14             This plugin generates a JSON data representation.
15              
16             =head1 FILE OR STRING
17              
18             If a JSON filename is C or C<''>, this module returns a JSON
19             string instead of generating a JSON file.
20              
21             $feed->call( DumpJSON => 'feed.json' ); # generates a JSON file
22             my $json = $feed->call( 'DumpJSON' ); # returns a JSON string
23              
24             =head1 OPTIONS
25              
26             This plugin allows some optoinal arguments following:
27              
28             my %opt = (
29             slim => 1,
30             slim_element_add => [ 'media:thumbnail@url' ],
31             slim_element => [ 'link', 'title', 'pubDate' ],
32             );
33             my $json = $feed->call( DumpJSON => %opt );
34              
35             =head2 slim
36              
37             This plugin converts the whole feed into JSON format by default.
38             All elements and attribuets are included in a JSON generated.
39             If this boolean is true, some limited elements are only included.
40              
41             =head2 slim_element_add
42              
43             An array reference for element/attribute names
44             which is given by set()/get() method's format.
45             These elements/attributes are also appended for slim JSON.
46              
47             =head2 slim_element
48              
49             An array reference for element/attribute names.
50             The default list of limited elements is replaced by this value.
51              
52             =head1 MODULE DEPENDENCIES
53              
54             L, L and L
55              
56             =head1 SEE ALSO
57              
58             JSON, JavaScript Object Notation:
59             L
60              
61             =head1 AUTHOR
62              
63             Yusuke Kawasaki, http://www.kawa.net/
64              
65             =head1 COPYRIGHT AND LICENSE
66              
67             Copyright (c) 2006-2008 Yusuke Kawasaki. All rights reserved.
68             This program is free software; you can redistribute it
69             and/or modify it under the same terms as Perl itself.
70              
71             =cut
72             # ----------------------------------------------------------------
73             package XML::FeedPP::Plugin::DumpJSON;
74 4     4   428228 use strict;
  4         13  
  4         178  
75 4     4   23 use vars qw( @ISA );
  4         8  
  4         240  
76             @ISA = qw( XML::FeedPP::Plugin );
77 4     4   24 use Carp;
  4         8  
  4         304  
78 4     4   22 use Symbol;
  4         8  
  4         274  
79             require 5.008;
80 4     4   22 use JSON::Syck;
  4         6  
  4         259  
81             # use JSON::PP;
82             # use JSON::XS;
83              
84 4     4   18 use vars qw( $VERSION );
  4         6  
  4         5279  
85             $VERSION = "0.33";
86              
87             *XML::FeedPP::to_json = \&to_json;
88              
89             my $SLIM_ELEM = [qw(
90             link title pubDate dc:date modified issued dc:subject category
91             image/url media:content@url media:thumbnail@url
92             )];
93             my $DEFAULT_OPTS = {
94             slim_element => undef,
95             slim_element_add => undef,
96             utf8_flag => undef,
97             use_json_syck => 1,
98             use_json_pp => undef,
99             };
100              
101             sub run {
102 13     13 0 456318 my $class = shift;
103 13         27 my $feed = shift;
104 13         42 &to_json( $feed, @_ );
105             }
106              
107             sub to_json {
108 13     13 0 21 my $data = shift;
109 13 100       71 my $file = shift if scalar @_ % 2; # odd arguments
110 13         83 my $opts = { %$DEFAULT_OPTS, @_ };
111 13 50       56 $file = $opts->{file} if exists $opts->{file};
112              
113             # cut some elements out
114 13 100 100     149 if ( $opts->{slim} || $opts->{slim_element} || $opts->{slim_element_add} ) {
      66        
115 6         20 $data = &slim_feed( $data, $opts->{slim_element}, $opts->{slim_element_add} );
116             }
117              
118             # perl object to json
119 13         38 my $json = &dump_json( $data, $opts );
120              
121             # json to file
122 13 50       79 if ( $file ) {
123 0         0 &write_file( $file, $json, $opts );
124             }
125 13         471 $json;
126             }
127              
128             sub write_file {
129 0     0 0 0 my $file = shift;
130 0         0 my $fh = Symbol::gensym();
131 0 0       0 open( $fh, ">$file" ) or Carp::croak "$! - $file";
132 0         0 print $fh @_;
133 0         0 close($fh);
134             }
135              
136             sub dump_json {
137 13     13 0 34 my $data = shift;
138 13         22 my $opts = shift;
139              
140 13         27 my $usesyck = $opts->{use_json_syck};
141 13         22 my $usepp = $opts->{use_json_pp};
142 13 50       35 $usesyck = 1 unless $usepp;
143 13 50       30 $usepp = 1 unless $usesyck;
144              
145 13 50 33     74 if ( $usesyck && defined $JSON::Syck::VERSION ) {
146 13         30 return &dump_json_syck($data,$opts);
147             }
148 0 0 0     0 if ( $usepp && defined $JSON::VERSION ) {
149 0         0 return &dump_json_pm($data,$opts);
150             }
151 0 0       0 if ( $usesyck ) {
152 0         0 local $@;
153 0         0 eval { require JSON::Syck; };
  0         0  
154 0 0       0 return &dump_json_syck($data,$opts) unless $@;
155             }
156 0 0       0 if ( $usepp ) {
157 0         0 local $@;
158 0         0 eval { require JSON; };
  0         0  
159 0 0       0 return &dump_json_pm($data,$opts) unless $@;
160             }
161 0 0       0 if ( $usepp ) {
162 0         0 Carp::croak "JSON::PP or JSON::Syck is required";
163             }
164             else {
165 0         0 Carp::croak "JSON::Syck is required";
166             }
167             }
168              
169             sub dump_json_syck {
170 13     13 0 20 my $data = shift;
171 13         46 my $opts = shift;
172             # warn "[JSON::Syck $JSON::Syck::VERSION]\n";
173 13 50       54 local $JSON::Syck::ImplicitUnicode = $opts->{utf8_flag} if exists $opts->{utf8_flag};
174             # local $JSON::Syck::SingleQuote = 0;
175 13         47751 JSON::Syck::Dump($data);
176             }
177              
178             sub dump_json_pm {
179 0     0 0 0 my $data = shift;
180 0         0 my $opts = shift;
181              
182 0         0 my $ver = ( $JSON::VERSION =~ /^([\d\.]+)/ )[0];
183 0 0       0 Carp::croak "JSON::PP is not correctly loaded." unless $ver;
184 0 0       0 return &dump_json_pp1($data,$opts) if ( $ver < 1.99 );
185 0         0 return &dump_json_pp2($data,$opts);
186             }
187              
188             sub dump_json_pp2 {
189 0     0 0 0 my $data = shift;
190 0         0 my $opts = shift;
191 0 0       0 if ( ! defined $JSON::PP::VERSION ) {
192 0         0 local $@;
193 0         0 eval { require JSON::PP; };
  0         0  
194 0 0       0 Carp::croak "JSON::PP is required" if $@;
195             }
196             # warn "[JSON::PP $JSON::PP::VERSION]\n";
197 0         0 my $json = JSON::PP->new();
198 0 0       0 my $utf8 = $opts->{utf8_flag} if exists $opts->{utf8_flag};
199 0 0       0 my $bool = $utf8 ? 0 : 1;
200 0         0 $json->utf8($bool);
201 0         0 $json->allow_blessed(1);
202 0         0 $json->as_nonblessed(1);
203 0         0 $json->encode($data);
204             }
205              
206             sub dump_json_pp1 {
207 0     0 0 0 my $data = shift;
208 0         0 my $opts = shift;
209             # warn "[JSON $JSON::VERSION]\n";
210 0         0 my $json = JSON->new();
211 0 0       0 my $utf8 = $opts->{utf8_flag} if exists $opts->{utf8_flag};
212 0 0       0 local $JSON::UTF8 = $utf8 ? 0 : 1;
213 0         0 $json->convblessed(1);
214 0         0 $json->objToJson($data)
215             }
216              
217             sub slim_feed {
218 6     6 0 12 my $feed = shift;
219 6   66     26 my $list = shift || $SLIM_ELEM;
220 6         7 my $add = shift;
221 6         9 my $slim = {};
222 6         18 my $root = ( keys %$feed )[0];
223 6 100       17 if ( ref $add ) {
224 3         11 $list = [ @$list, @$add ];
225             }
226 6         12 my $channel = {};
227 6         14 foreach my $key ( @$list ) {
228 45 100       140 my $val = ( $key eq "link" ) ? $feed->link() : $feed->get($key);
229 45 100       732 $channel->{$key} = $val if defined $val;
230             }
231 6         155 my $entries = [];
232 6         37 foreach my $item ( $feed->get_item() ) {
233 12         84 my $hash = {};
234 12         18 foreach my $key ( @$list ) {
235 90 100       241 my $val = ( $key eq "link" ) ? $item->link() : $item->get($key);
236 90 100       1022 $hash->{$key} = $val if defined $val;
237             }
238 12         29 push( @$entries, $hash );
239             }
240 6         13 my $data;
241 6 100       21 if ( $root eq 'rss' ) {
    100          
    50          
242 2         3 $channel->{item} = $entries;
243 2         7 $data = { rss => { channel => $channel }};
244             }
245             elsif ( $root eq 'rdf:RDF' ) {
246 2         6 $data = { 'rdf:RDF' => { channel => $channel, item => $entries }};
247             }
248             elsif ( $root eq 'feed' ) {
249 2         5 $channel->{entry} = $entries;
250 2         6 $data = { feed => $channel };
251             }
252             else {
253 0         0 Carp::croak "Invalid feed type: $root";
254             }
255 6         21 $data;
256             }
257              
258             # ----------------------------------------------------------------
259             1;
260             # ----------------------------------------------------------------