Базовый анализ строки XML с XML::Twig
Я использую XML::Simple более десяти лет, и он сделал все, что мне нужно, и я почти никогда не трогал Perl. Хотя сейчас мне нужно проанализировать строку XML, чтобы просто: получить все элементы, являющиеся дочерними элементами корня, и для каждого получить их тип элемента, атрибуты и содержимое (мне все равно, есть ли вложенные элементы, просто чтение содержимого в виде строки идеально). Я могу сделать все это с помощью XML::Simple, ИСКЛЮЧИТЬ, кроме того, мне нужно сохранить порядок, который Simple не может сделать, когда есть несколько типов элементов.
Я только что установил Twig, и это выглядит потрясающе для чего-то, на что я надеялся, что это будет быстрый сценарий. Маловероятно, что я когда-нибудь снова буду использовать Twig, это то, что Twig может сделать легко?
3 ответа
На простом уровне - XML::Twig
- прохождение детей:
#!/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>
Код ниже должен дать вам достаточно информации, чтобы начать.
Несколько заметок:
- разобрать файл использовать
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:: 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";
}
}