使用 XML::Twig 对 XML 字符串进行基本解析

Basic parsing of XML string with XML::Twig

我已经使用 XML::Simple 十多年了,它已经完成了我需要的一切,而且我几乎没有再接触过 Perl。尽管现在我需要将一个 XML 字符串解析为简单地:获取作为根的子元素的所有元素,并为每个元素获取它们的元素类型、属性和内容(我不关心是否有任何嵌套元素,只需将内容作为字符串读取即可)。我可以使用 XML::Simple 完成所有这些工作,除了我还需要保持顺序,当有多种元素类型时,Simple 无法做到这一点。

我刚刚安装了 Twig,它看起来非常适合我希望能成为一个快速脚本的东西。这之后我不太可能再使用 Twig,这是 Twig 可以轻松做到的事情吗?

我更喜欢XML::LibXML。它的Reader不需要将整个结构保存在内存中,因此它可以处理大文件:

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

use XML::LibXML::Reader;

my $reader = 'XML::LibXML::Reader'->new( location => 'file.xml' );
while ($reader->read) {
    if (1 == $reader->depth
        and XML_READER_TYPE_ELEMENT == $reader->nodeType
       ) {
        my @info = ($reader->name);
        my $inner = $reader->readInnerXml;
        for my $idx (0 .. $reader->attributeCount - 1) {
            $reader->moveToAttributeNo($idx);
            push @info, $reader->name . '=' . $reader->value;
        }
        push @info, $inner;
        print "@info\n";
    }
}

下面的代码应该为您提供了足够的信息来开始。

一些注意事项:

  • 解析文件使用 parsefile 而不是 parse
  • 你也可以用'level(1)'代替'/root/*'
  • 使用闭包调用处理程序 (process_elt),传递 $atts$strings 是执行此操作的干净方法,如果您想要 $atts$strings 要成为全局变量,你可以只写 '/root/*' => \&process_elt 并且将使用树枝和元素作为参数调用处理程序
  • $t->purge 位用于释放刚刚处理的元素所占用的内存,如果文件太大无法放入内存,这很有用,否则您不需要使用它
  • DDPData::Printer,它只是用来检查输出,您可以使用任何其他方式来执行此操作(Data::DumperYAML、打印.. .)

代码如下:

#!/usr/bin/perl

use strict;
use warnings;

use XML::Twig;

my $atts    = []; # attributes
my $strings = []; # text content

XML::Twig->new( twig_handlers => 
                 { '/root/*' => sub { process_elt( @_, $strings, $atts); } })
         ->parse( \*DATA);

use DDP; p $atts; p $strings;

sub process_elt
  { my( $t, $elt, $strings, $atts)= @_;

    push @$atts, $elt->atts;

    my $string= $elt->text;
    if( $elt->tag eq 'e1')
      { $string=~ s{text}{modified}; }
    push @$strings, $string;

    $t->purge;
  }

__DATA__
<root>
  <e1 att_1="val_1_1" att2= "val_2_1">text content of element 1</e1>
  <e1 att_1="val_1_2" att2= "val_2_2">text content of element 2</e1>
  <e2 att_3="val_3_1" att2= "val_2_3">element with <sub_elt>sub element</sub_elt> inside</e2>
</root>

简单来说 - XML::Twig - 遍历 children:

#!/usr/bin/perl

use strict;
use warnings; 

use XML::Twig;

my $twig = XML::Twig -> new -> parsefile ( 'myxml.xml' );

foreach my $element ( $twig -> root -> children ) { 
    print $element -> text; #element content. 
}

提取元素属性可以通过以下方式完成:

 $element -> att('attributename');

或者您可以使用 atts:

获取哈希引用
 my $attributes = $element -> atts();
 foreach my $key ( keys %$attributes ) {
     print "$key => ", $attributes -> {$key}, "\n";
 }

不过,我特别喜欢的一点是,对于 XML,您有一长串类似的元素,您尝试处理的地方 - 您可以定义一个处理程序 - 每次调用解析器遇到并被传递给 XML.

的子集
sub process_book {
     my ( $twig, $book )  = @_;
     print $book -> first_child ('title'); 
     $twig -> purge; #discard anything we've already seen. 
}

my $twig = XML::Twig -> new ( twig_handlers => { 'book' => \&process_book } ); 
$twig -> parsefile ( 'books.xml' ); 

样本XML:

<XML>
   <BOOK>
       <title>Elements of style</title>
       <author>Strunk and White</author>
   </BOOK>
</XML>