我有XML文件(某些应用程序的输出),如下所示:
<data>
<call function="get_user_data">
<output>
<integer name="my_id" value="-31" />
<string name="login" value="root" />
<integer name="ip_list" value="2" />
<array name="i">
<item>
<ip_address name="user_ip" value="0.0.0.0" />
<ip_address name="user_mask" value="0.0.0.0"/>
</item>
<item>
<ip_address name="user_ip" value="94.230.160.230" />
<ip_address name="user_mask" value="255.255.255.0"/>
</item>
</array>
<integer name="modules" value="2" />
<array name="i">
<item>
<string name="module_name" value="core" />
</item>
<item>
<string name="module_name" value="devices" />
</item>
</array>
<integer name="addition_modules" value="0"/>
<array name="i"/>
</output>
</call>
</data>
我需要为这个perl结构解析它:
$data = {
my_id => -31,
login => "root",
ip_list => [
{
user_ip => "0.0.0.0",
user_mask => "0.0.0.0"
},
{
user_ip => "94.230.160.230",
user_mask => "255.255.255.0"
}
],
modules => [
{
module_name => "core"
},
{
module_name => "devices"
}
],
addition_modules => []
}
帮帮我,PLZ,去做吧!
答案 0 :(得分:12)
这是一种糟糕的XML格式。这是多余的:为什么给出每个数组的元素数量,它就在那里,并且层次结构没有很好地定义:数组的名称(如果你必须拥有它,它的元素数量)应该是数组的属性,而不是它在同一级别之前的元素。
无论如何......你的格式是如此具体,我怀疑你可以在这里使用通常的XML :: Simple,所以我拿出了XML :: Twig,我认为这样做:
#!/usr/bin/perl
use strict;
use warnings;
use XML::Twig;
use Test::More tests => 1;
my $expected= {
my_id => -31,
login => "root",
ip_list => [
{ user_ip => "0.0.0.0",
user_mask => "0.0.0.0"
},
{ user_ip => "94.230.160.230",
user_mask => "255.255.255.0"
}
],
modules => [
{ module_name => "core" },
{ module_name => "devices" }
],
addition_modules => []
};
my $data={};
my $t=XML::Twig->new( twig_handlers => { 'integer[@name="my_id"]' => sub { add_field( $data, $_)},
'string[@name="login"]' => sub { add_field( $data, $_)},
array => sub { array( $data, @_); },
},
)
->parse( \*DATA); # replace with parsefile( 'file.xml') to parse a file
is_deeply( $data, $expected, 'one test to rule them all');
sub array
{ my( $data, $t, $array)= @_;
my $name= $array->prev_sibling( 'integer')->att( 'name');
$data->{$name}=[];
foreach my $item ($array->children( 'item'))
{ my $item_data={};
foreach my $child ($item->children)
{ add_field( $item_data, $child); }
push @{$data->{$name}}, $item_data;
}
}
# get a name/value pair of attributes and add it to a hash, which
# could be the overall $data or an element in an array
sub add_field
{ my( $data, $elt)= @_;
$data->{$elt->att( 'name')}= $elt->att( 'value');
}
__DATA__
<data>
<call function="get_user_data">
<output>
<integer name="my_id" value="-31" />
<string name="login" value="root" />
<integer name="ip_list" value="2" />
<array name="i">
<item>
<ip_address name="user_ip" value="0.0.0.0" />
<ip_address name="user_mask" value="0.0.0.0"/>
</item>
<item>
<ip_address name="user_ip" value="94.230.160.230" />
<ip_address name="user_mask" value="255.255.255.0"/>
</item>
</array>
<integer name="modules" value="2" />
<array name="i">
<item>
<string name="module_name" value="core" />
</item>
<item>
<string name="module_name" value="devices" />
</item>
</array>
<integer name="addition_modules" value="0"/>
<array name="i"/>
</output>
</call>
</data>