複数dc:subjectのRSSを読み込む、再び。XML::RSS::LibXML編
ほとんどあきらめてたXML::RSSでの複数dc:subject問題ですが、昨日の朝「下から見るエンジニアリング」を読んで惚れこんでしまったついでにXML::RSS::LibXMLソース見て、なるほど読みやすい、と思いながら眺めてたらXML::RSS::LibXMLなら少しいじるだけで複数dc:subject問題を解決できそうな気がしてきたのでいじってみました。
XML::RSS::LibXML::Parser.pmを以下のように修正してみると、
--- Parser.pm.org 2005-11-02 07:08:55.000000000 +0900 +++ Parser.pm 2005-11-03 05:47:38.759503200 +0900 @@ -219,6 +219,9 @@ my $xpath = $prefix eq $vprefix ? "./*[not(contains(name(), ':'))]" : "./*[starts-with(name(), '$prefix:')]"; + # temporay nodes hash for plural node. + my %plural_nodes; + # now, for each node that we can cover, go and parse foreach my $node ($xc->findnodes($xpath, $root)) { if ($xc->findnodes('./*', $node)) { @@ -231,17 +234,30 @@ $text = ''; } + my $tmp_node; # argh. it has attributes. we do our little hack... if ($node->hasAttributes) { - $sub{$node->localname} = XML::RSS::LibXML::MagicElement->new( + $tmp_node = XML::RSS::LibXML::MagicElement->new( content => $text, attributes => [ $node->attributes ] ); } else { - $sub{$node->localname} = $text; + $tmp_node = $text; + } + + if (defined $sub{$node->localname}) { + if (!defined $plural_nodes{$node->localname}) { + push @{$plural_nodes{$node->localname}}, $sub{$node->localname}; + } + push @{$plural_nodes{$node->localname}}, $tmp_node; + } else { + $sub{$node->localname} = $tmp_node; } } } + foreach my $key (keys %plural_nodes) { + $sub{$key} = $plural_nodes{$key}; + } if (keys %sub) { # If this is a native RSS element, we just need to assign to
print Dumper $rssするとdc:subject部が配列になっていて成功しているようです。見やすいコードはフィードバックも得られやすいかも、と感じました。
しかし、クライアント側で複数か否かを判断するコードがダサいです。
#!/usr/local/bin/perl -w use strict; use warnings; use lib "./lib"; use LWP::Simple; use XML::RSS::LibXML; my $url = 'http://b.hatena.ne.jp/koyachi/rss'; my $content = get( $url ); my $rss = XML::RSS::LibXML->new; $rss->parse( $content ); my $output; foreach my $item ( @{$rss->{items}} ) { $output .= "--------------------\n"; $output .= "title : $item->{title}\n"; $output .= "link : $item->{link}\n"; $output .= "description : $item->{description}\n"; $output .= "dc:subject : \n"; if ( ref($item->{dc}->{subject}) eq 'ARRAY' ) { foreach my $subject ( @{$item->{dc}->{subject}} ) { $output .= " $subject\n"; } } else { $output .= " $item->{dc}->{subject}"; } } print $output;
というかあるときはSCALARであるときはARRAYでっていうインターフェースがダメだとは思うんだけど僕のレベルではどうすべきかわかりません。そもそもXML::RSSとの互換しつつ高速化することが目的のはずなのでその点からしてもダメですね。教えてエロイ人!