在Perl中解析XML文档

时间:2012-07-06 14:52:08

标签: perl xml-parsing

我有一个奇怪的XML文档,按以下方式排列

<a>
   <b>
     <c c1="blah" c2="blah">
        <d d1="blah0" d2="blah1" d3="blah2" d4="blah3" d5="blah4" />
        <d d1="blah5" d2="blah6" d3="blah7" d4="blah8" d5="blah9" />
        <d d1="blah10" d2="blah11" d3="blah12" d4="blah13" d5="blah14" />
     </c>
     <c c1="blahc" c2="blah">
        <d d1="blah0" d2="blah1" d3="blah2" d4="blah3" d5="blah4" />
        <d d1="blah5" d2="blah6" d3="blah7" d4="blah8" d5="blah9" />
        <d d1="blah10" d2="blah11" d3="blah12" d4="blah13" d5="blah14" />
     </c>
    ...
  <b>
    ....
  </b>
  <e/>
</a>

我想为所有d2个节点中的所有d4个节点提取d5cb的值。

我尝试使用XML::Simple并在阵列引用方面遇到了很多困难。 我尝试使用XML::DOM,但考虑到我的XML文件大小为500MB,它似乎不是一个好选择。我建议一个好方法,因为我是Perl的新手

3 个答案:

答案 0 :(得分:2)

您的问题有点令人困惑,您需要d元素的属性,而不是c元素的属性。或者您可能想要属性的值,无论c下的元素是什么。

在任何情况下,特别是如果文件很大,这似乎是XML::Twig的良好匹配:

#!/usr/bin/perl

use strict;
use warnings;

use XML::Twig;

XML::Twig->new( twig_handlers => { 'b/c/*' => \&get_atts })
         ->parse( \*DATA); # replace by parsefile( 'my.xml') 

sub get_atts
  { my( $t, $elt)= @_;
    foreach my $att ( qw( d2 d4 d5))
      { print "$att: ", $elt->att( $att), " "; }
    print "\n";
    $t->purge; # this frees the memory so you keep at most 1 d element 
  }

__DATA__
<a>
   <b>
     <c c1="blah" c2="blah">
        <d d1="blah0" d2="blah1" d3="blah2" d4="blah3" d5="blah4" />
        <d d1="blah5" d2="blah6" d3="blah7" d4="blah8" d5="blah9" />
        <d d1="blah10" d2="blah11" d3="blah12" d4="blah13" d5="blah14" />
     </c>
     <c c1="blahc" c2="blah">
        <d d1="blah0" d2="blah1" d3="blah2" d4="blah3" d5="blah4" />
        <d d1="blah5" d2="blah6" d3="blah7" d4="blah8" d5="blah9" />
        <d d1="blah10" d2="blah11" d3="blah12" d4="blah13" d5="blah14" />
     </c>
  </b>
  <b>
  </b>
  <e/>
</a>

如果属性始终位于d元素中,请将'b/c/*'替换为'b/c/d',这样会更有效。

答案 1 :(得分:1)

使用xsh

for a/b/c/d ls (@d2 | @d4 | @d5);

更新:(对于mirod):使用Perl中的XML :: XSH2不太优雅,但仍可以使用 -

#!/usr/bin/perl
use strict;
use warnings;

use XML::XSH2;

xsh q{
    open 1.xml ;
    for /a/b/c/d {
        for my $attr in (@d2 | @d4 | @d5) {
            perl { push @ar, $attr }
        }
    }
};

printf "%s:%s\n", $_->name, $_->value for @XML::XSH2::Map::ar;

或者,让Perl为您编写xsh代码:

#!/usr/bin/perl
use warnings;
use strict;

use XML::XSH2;

xsh 'open 1.xml';
xsh '$attributes = (' . join('|', map 'a/b/c/@d' . $_, 1, 2, 4) . ')';
for (@$XML::XSH2::Map::attributes) {
    print $_->name, '=', $_->value, "\n";
}

答案 2 :(得分:1)

CPAN中有许多XML模块可以帮助您解决这个问题,但在这种情况下我的资金是XML::XPath,它允许您简洁地描述您想要从XML中提取的数据。

此程序使用您的示例数据并提供我认为您想要的输出(尽管严格来说,任何d="xx"个节点都没有<c>属性)。

use strict;
use warnings;

use feature 'say';

use XML::XPath;

my $xml = XML::XPath->new(ioref => \*DATA);

for my $cnode ($xml->find('//b/c/d')->get_nodelist) {
  for ($cnode->find('@d2|@d4|@d5')->get_nodelist) {
    print $_->getData, "\n";
  }
}

__DATA__
<a>
   <b>
     <c c1="blah" c2="blah">
        <d d1="blah0" d2="blah1" d3="blah2" d4="blah3" d5="blah4" />
        <d d1="blah5" d2="blah6" d3="blah7" d4="blah8" d5="blah9" />
        <d d1="blah10" d2="blah11" d3="blah12" d4="blah13" d5="blah14" />
     </c>
     <c c1="blahc" c2="blah">
        <d d1="blah0" d2="blah1" d3="blah2" d4="blah3" d5="blah4" />
        <d d1="blah5" d2="blah6" d3="blah7" d4="blah8" d5="blah9" />
        <d d1="blah10" d2="blah11" d3="blah12" d4="blah13" d5="blah14" />
     </c>
    ...
  </b>
  <e/>
</a>

<强>输出

blah1
blah3
blah4
blah6
blah8
blah9
blah11
blah13
blah14
blah1
blah3
blah4
blah6
blah8
blah9
blah11
blah13
blah14