我正在尝试使用XML :: LibXML模块解析XML。 XML数据结构具有名为<row>
的节点,该节点包含两个子节点<key>
和<value>
。我想解析这些<row>
中的每一个并构建一个哈希数据结构。我可以拿出下面的代码来实现它,但我觉得有更好的方法来实现它。
use strict;
use warnings;
use Data::Dumper;
use XML::LibXML;
my $XML=<<EOF;
<config>
<row>
<key>
<A1>alpha</A1>
<A2>beta</A2>
<A3>cat</A3>
<A4>delta</A4>
</key>
<value>
<B1>eclipse</B1>
<B2>pico</B2>
<B3>penta</B3>
<B4>zeta</B4>
</value>
</row>
<row>
<key>
<A1>tom</A1>
<A2>harry</A2>
<A3>bob</A3>
<A4>ben</A4>
</key>
<value>
<B1>TAP</B1>
<B2>MAN</B2>
<B3>WORK</B3>
<B4>MAINTAIN</B4>
</value>
</row>
</config>
EOF
my $parser = XML::LibXML->new();
my $doc = $parser->parse_string($XML);
my %hash;
my $i = 1;
foreach my $node ($doc->findnodes('/config/row/key')) {
foreach my $tag ('A1', 'A2','A3','A4') {
$hash{'KEY' . $i}{$tag} = $node->findvalue( $tag );
}
$i++;
}
$i = 1;
foreach my $node ($doc->findnodes('/config/row/value')) {
foreach my $tag ('B1', 'B2','B3','B4') {
$hash{'KEY' . $i}{$tag} = $node->findvalue( $tag );
}
$i++;
}
print Dumper \%hash;
输出
$VAR1 = {
'KEY2' => {
'A3' => 'bob',
'B3' => 'WORK',
'B1' => 'TAP',
'A1' => 'tom',
'B4' => 'MAINTAIN',
'B2' => 'MAN',
'A2' => 'harry',
'A4' => 'ben'
},
'KEY1' => {
'A3' => 'cat',
'B3' => 'penta',
'B1' => 'eclipse',
'A1' => 'alpha',
'B4' => 'zeta',
'B2' => 'pico',
'A2' => 'beta',
'A4' => 'delta'
}
};
实际上,我不想创建虚构的键(KEY1,KEY2 ..),而是希望<A1>
节点的值被视为每个部分的键。有人可以帮帮我吗。
期望的输出:
'tom' => {
'A3' => 'bob',
'B3' => 'WORK',
'B1' => 'TAP',
'B4' => 'MAINTAIN',
'B2' => 'MAN',
'A2' => 'harry',
'A4' => 'ben'
},
'alpha' => {
'A3' => 'cat',
'B3' => 'penta',
'B1' => 'eclipse',
'B4' => 'zeta',
'B2' => 'pico',
'A2' => 'beta',
'A4' => 'delta'
}
答案 0 :(得分:2)
&#34;我希望将<A1>
个节点的值视为每个部分的关键字&#34;
此解决方案为每个行元素创建一个哈希值,并将其推送到@rows
数组。与原始文件不同,它从名为config.xml
忽略A*
和B*
元素的标记 - 只是假设键和值的顺序相同
主循环遍历row
元素,对于每一行,key
和value
子元素的列表将转换为其文本值{{1} }。然后构建一个哈希并将其推送到数组
我已使用map
显示结果数据结构,因为我认为它远远优于Data::Dump
Data::Dumper
use strict;
use warnings;
use XML::LibXML;
my $doc = XML::LibXML->load_xml( location => 'config.xml' );
my @rows;
for my $row ($doc->findnodes('/config/row')) {
my @keys = map $_->textContent, $row->findnodes('key/*');
my @values = map $_->textContent, $row->findnodes('value/*');
my %row;
@row{@keys} = @values;
push @rows, \%row;
}
use Data::Dump;
dd \@rows;
此处的变体符合您的所需输出。感谢choroba向我指出
它与我上面的原始方法非常相似,但是它构建了一个哈希而不是一个数组并使用了这些元素&#39;标记名称作为键而不是我猜想你想要的键/值关系
我应该说我对您选择的数据结构非常怀疑;例如,我认为不需要从子哈希中排除[
{ alpha => "eclipse", beta => "pico", cat => "penta", delta => "zeta" },
{ ben => "MAINTAIN", bob => "WORK", harry => "MAN", tom => "TAP" },
]
密钥,因为它的值用于标识行。如果使用A1
和key
字符串作为键和值不会更好,我也会感到惊讶。但也可能是XML标签名称选择错误,您的选择是最佳的,我无法知道
这是Perl代码。它像以前一样从value
文件中读取。如果您希望按照我的描述保留config.xml
哈希元素,那么您只需将A1
更改为elsif
就可以了
if
use strict;
use warnings;
use XML::LibXML;
my $doc = XML::LibXML->load_xml( location => 'config.xml' );
my ( %data, $section);
for my $row ( $doc->findnodes('/config/row') ) {
for my $item ( $row->findnodes('key/* | value/*') ) {
my ($key, $val) = ( $item->tagName, $item->textContent );
if ( defined $section ) {
$data{$section}{$key} = $val
}
else {
$section = $val;
}
}
}
use Data::Dump;
dd \%data;
答案 1 :(得分:1)
第一个XPath表达式选择A1,第二个选择同一行中的所有A *和B *(A1本身除外)。
#! /usr/bin/perl
use warnings;
use strict;
use XML::LibXML;
my $xmlstring = << '__XML__';
<config>
...
</config>
__XML__
my $xml = 'XML::LibXML'->load_xml(string => $xmlstring);
my $root = $xml->documentElement;
my %hash;
for my $a1 ($root->findnodes('/config/row/key/A1')) {
for my $node ($a1->findnodes('(../../key/*[not(self::A1)] | ../../value/*)')) {
$hash{ $a1->textContent }{ $node->getName } = $node->textContent;
}
}
use Data::Dump;
dd \%hash;
{
alpha => {
A2 => "beta",
A3 => "cat",
A4 => "delta",
B1 => "eclipse",
B2 => "pico",
B3 => "penta",
B4 => "zeta",
},
tom => {
A2 => "harry",
A3 => "bob",
A4 => "ben",
B1 => "TAP",
B2 => "MAN",
B3 => "WORK",
B4 => "MAINTAIN",
},
}