/XML-FeedPP-Plugin-DumpJSON/lib/XML/FeedPP/Plugin/DumpJSON.pm

http://xml-treepp.googlecode.com/ · Perl · 260 lines · 245 code · 15 blank · 0 comment · 2 complexity · 3babc3a5809211c1c47969c6a743a9e5 MD5 · raw file

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