PerlでXMLをハッシュ形式に変換する
PerlでXMLをハッシュに変換してベンチをとってみました。
XML::Simpleは遅いよって記事を見かけたので頑張ってXML::libXMLで再帰的にハッシュリファレンスに突っ込んでくコード書いたのですが、
ベンチをとったらXML::Simpleの方が早かった…。
コードがおかしいのかなんなのか…。
よくわからない。
ちなみにXML::Simpleはテスト込みで実装10分、XML::libXMLはてこずって2日間かかった\(^o^)/
途中このPDFにとてもお世話になりました。
http://refcards.com/docs/forda/perl-xml-libxml/perl-xml-libxml-refcard-a4.pdf
結果
Benchmark: timing 10000 iterations of XML::LibXML, XML::Simple... XML::LibXML: 7 wallclock secs ( 6.14 usr + 0.00 sys = 6.14 CPU) @ 1628.66/s (n=10000) XML::Simple: 5 wallclock secs ( 5.21 usr + 0.01 sys = 5.22 CPU) @ 1915.71/s (n=10000)
検証に使ったXML
<?xml version="1.0" encoding="UTF-8"?> <Response> <status>Success</status> <count>10</count> <url>http://hoge.com/fuga</url> <name>Item</name> <infomation> <info> <infoText>TXT</infoText> <infoImg>http://hoge.com/hige.gif</infoImg> </info> <infoText>TXT</infoText> <infoImg>http://hoge.com/hige.gif</infoImg> </infomation> </Response>
ベンチに使ったコード
#!/usr/bin/perl use strict; use warnings; use Benchmark qw(timethese); use XML::LibXML; use XML::Simple; $XML::Simple::PREFERRED_PARSER = 'XML::Parser'; use HTTP::Request::Common qw(POST); use LWP::UserAgent; use Data::Dumper; $Data::Dumper::Indent = 1; $Data::Dumper::Sortkeys = 1; $Data::Dumper::Terse = 1; my $num = $ARGV[0] || 1000; my $uri = q{http://hoge.fuga.com/test.xml}; my $req = POST ($uri); my $ua = LWP::UserAgent->new; $ua->timeout(10); my $response = $ua->request($req); timethese($num, { 'XML::Simple' => \&with_xml_simple, 'XML::LibXML' => \&with_xml_lib_xml, }); sub with_xml_simple { my $hash_ref = XML::Simple->new(SuppressEmpty => undef)->XMLin($response->content); } sub with_xml_lib_xml { my $parser = XML::LibXML->new(); my $dom = $parser->parse_string($response->content); my $hash_ref = {}; my $xpath_base = '/Response'; my @nodes = $dom->findnodes($xpath_base . '/*'); foreach my $node (@nodes) { &_parse_xml($hash_ref, $node, $xpath_base . '/' . $node->nodeName); } } sub _parse_xml { my ($data, $node, $xpath) = @_; my $base_node_name = $node->nodeName; my @nodes = $node->findnodes($xpath . '/*'); if (@nodes) { foreach my $child (@nodes) { $data->{$base_node_name} = {} unless defined $data->{$base_node_name}; &_parse_xml( $data->{$base_node_name}, $child, sprintf("%s/%s", $xpath, $child->nodeName) ); } } else { $data->{$base_node_name} = $node->findvalue($xpath); } }