shimxmemo

メモをのこすよ!

PerlでXMLをハッシュ形式に変換する

PerlXMLをハッシュに変換してベンチをとってみました。

 

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);
  }
}