如何使用Perl XML :: LibXML使用findnodes和findvalue解析XML数据

时间:2015-08-03 18:19:36

标签: xml perl

我正在尝试使用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'
           }

2 个答案:

答案 0 :(得分:2)

&#34;我希望将<A1>个节点的值视为每个部分的关键字&#34;

此解决方案为每个元素创建一个哈希值,并将其推送到@rows数组。与原始文件不同,它从名为config.xml

的文件中读取XML数据

忽略A*B*元素的标记 - 只是假设键和值的顺序相同

主循环遍历row元素,对于每一行,keyvalue子元素的列表将转换为其文本值{{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" }, ] 密钥,因为它的值用于标识行。如果使用A1key字符串作为键和值不会更好,我也会感到惊讶。但也可能是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",
  },
}