使用 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
位用于释放刚刚处理的元素所占用的内存,如果文件太大无法放入内存,这很有用,否则您不需要使用它
DDP
是 Data::Printer
,它只是用来检查输出,您可以使用任何其他方式来执行此操作(Data::Dumper
、YAML
、打印.. .)
代码如下:
#!/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>
我已经使用 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
位用于释放刚刚处理的元素所占用的内存,如果文件太大无法放入内存,这很有用,否则您不需要使用它DDP
是Data::Printer
,它只是用来检查输出,您可以使用任何其他方式来执行此操作(Data::Dumper
、YAML
、打印.. .)
代码如下:
#!/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>