ブログ  前の記事  次の記事  2007-04-02 

たけまる / フィードから JSON への変換方法を統一して JavaScript でラクする


2007-04-02

_ フィードから JSON への変換方法を統一して JavaScript でラクする [atom][perl]

以前のエントリ [2007-03-31] で XML::FeedPP を使ったのは,まえに
JSONP 変換を作ったからです.

ところが,JSONP 変換サービスはあちこちにあります.たとえば,↓など
は老舗です.

XML を JSON に変換するサービス - ベータ版を公開 :: Drk7jp

では,どうして新たに JSONP 変換スクリプトを書いたかというと,変換
後の JSON のデータ構造を揃えたかったからです.

たとえば,RSS と Atom が混ざっていると,JavaScript でエントリを取
得するときにハッシュキーを使い分けなければなりません.また,XML か
ら JSON への変換方法が統一されていないと,ある要素が配列なのかハッ
シュなのかわからなかったりします.

というわけで,次のような特徴を持つ JSONP 変換スクリプトを作ってみ
ました.公開しますので自由に使ってください.

■ 特徴
- データ形式を指定したフィード (たとえば Atom 1.0) に変換してから
  JSON にする.フィードの種類がなんであっても,JavaScript で同じよ
  うに処理できる.
- XML から JSON への変換は,GData 準拠とする.たとえば,要素数がひ
  とつであればハッシュ,複数であれば配列となる.また,テキスト要素
  へのアクセスキーは '$t' である.
- 複数フィードをひとつにまとめられる.時刻順のソートもできる.
- ついでに,XML 間の変換もサポートした ([2007-03-31] に書いたよう
  に,Atom 0.3 <-> Atom 1.0 とか).

JSON への変換には,[2007-02-22] で紹介した Google::Data::JSON を使っ
ています.

まぁ,いまとなっては Yahoo PIPES でできることなんでしょうが,ビジ
ネス上の理由で使えなかったりすることもあるので,そういう方はこちら
を参考にしてください.下のほうでソースも公開しています.

■ ベースURI
http://teahut.sakura.ne.jp/t/feed.cgi

■ パラメータ
パラメータ 説明
uri フィードURI 複数指定するとひとつのフィードにまとめます
alt rdf, rss, atom03, atom10 変換後のデータ形式を指定します
jsonp JavaScript 関数名 この関数名で JSON を格納します
sort feed, date エントリをソートします

jsonp を指定しなければ,単なる XML 変換を行います.

■ 例
このブログのフィード (RDF) を Atom に変換して,JSON に変換し,
myfunc という JavaScript 関数 に渡します.

feed.cgi?alt=atom10&jsonp=myfunc&uri=http://teahut.sakura.ne.jp/b/cl.rdf

■ ソースコード
自由に使っていただいてかまいません.なお,XML::FeedPP には
[2007-03-31] のパッチを当ててください.

use strict;
use warnings;
use CGI;
use CGI::Carp qw( fatalsToBrowser );
use Google::Data::JSON qw( gdata add_elements );
use XML::FeedPP;

my @RDF_ELEMENTS = qw( channel description image item items link rdf:li rdf:RDF
		       rdf:Seq title url );
my @RSS_ELEMENTS = qw( author category channel cloud comments copyright 
		       description docs enclosure generator guid height image 
		       language lastBuildDate link managingEditor name pubDate
		       rating rss skipDays skipHours source textInput title ttl
		       url webMaster width );

add_elements( @RDF_ELEMENTS, @RSS_ELEMENTS );

my $PROXY = undef;

my $q = CGI->new;

$ENV{http_proxy} = $q->param('noproxy') ? undef : $PROXY;

my @uri = $q->param('uri');
my $alt = $q->param('alt') || 'rss';

my $feed = $alt eq 'rdf'       ? XML::FeedPP::RDF->new
         : $alt eq 'rss'       ? XML::FeedPP::RSS->new
         : $alt =~ /atom0\.?3/ ? XML::FeedPP::Atom->new
         :                       XML::FeedPP::Atom1_0->new ;

$feed->title( join ' - ', map { XML::FeedPP->new($_)->title } @uri );
$feed->merge($_) for @uri;
$feed->sort_item if $q->param('sort') && $q->param('sort') =~ /date/i;
$feed->pubDate(time);

$_->title( $_->title . ' (' . $_->author . ')' ) for $feed->get_item;

if ( my $jsonp = $q->param('jsonp') ) {
    print $q->header( -charset => 'utf-8', -type => 'application/json' ), 
          "$jsonp(" . gdata( $feed->to_string )->as_json . ')';
}
elsif ( $alt eq 'json' ) {
    print $q->header( -charset => 'utf-8', -type => 'application/json' ), 
          gdata( $feed->to_string )->as_json;
}
elsif ( $alt eq 'rdf' ) {
    print $q->header( -charset => 'utf-8', -type => 'application/rdf+xml' ), 
          $feed->to_string;
}
elsif ( $alt eq 'rss' ) {
    print $q->header( -charset => 'utf-8', -type => 'application/rss+xml' ), 
          $feed->to_string;
}
elsif ( $alt =~ /atom0\.?3/ ) {
    print $q->header( -charset => 'utf-8', -type => 'application/x.atom+xml' ), 
          $feed->to_string;
}
else {
    print $q->header( -charset => 'utf-8', -type => 'application/atom+xml' ), 
          $feed->to_string;
}

一言メッセージをこっそり送信できます (非公開)
 今年の西暦→
Referrer (Inside): [2007-06-23-1]