2015-03-01 2 views
2

Я использовал XML :: Simple уже более десяти лет, и он сделал все, что мне нужно, и я почти никогда не касался Perl. Хотя сейчас мне нужно просто проанализировать XML-строку: получить все элементы, являющиеся дочерними элементами корня, и для каждого получить их тип элемента, атрибуты и содержимое (меня не волнует, есть ли какие-либо вложенные элементы, просто чтение содержимого в виде строки идеально). Я могу сделать все это с помощью XML :: Simple EXCEPT. Мне также нужно сохранить порядок, который Simple не может сделать, когда существует несколько типов элементов.Базовый синтаксический разбор XML-строки с XML :: Twig

Я только что установил Twig, и это выглядит очень подавляющим для того, что я надеялся, это быстрый сценарий. Маловероятно, что после этого я снова смогу использовать Twig, это то, что Twig может сделать легко?

+0

вам нужен XML для содержания, или только содержание текста? – mirod

+0

и что вам нужно делать с данными? хранить его в переменной, печатать, выводить его в файл, на несколько файлов? И да, XML :: Twig может сделать это довольно простым способом. – mirod

+0

Последний вопрос, когда вы говорите «тип», вы имеете в виду имя элемента (тег)? – mirod

ответ

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> 
+0

Это очень помогает мне, но я не вижу, как понять, когда проходит через каждый элемент, как выяснить, что такое тип/имя этого элемента. Вы сказали 'print $ element -> text;' ... есть 'print $ element -> name;'? Потому что то, что я делаю с каждым элементом, зависит от имени элемента. Если это «p», я делаю одно, если это «h3», я делаю что-то еще, если это «inline», я делаю что-то еще и т. Д., И если это неизвестный тип элемента, он должен ошибиться и сообщить мне, что такое тип/имя элемента, чтобы я мог понять, как его обрабатывать. –

+0

Обновление, я просто попробовал '$ element -> name', и это сработало. –

+0

Текст - это контент за пределами тегов. Если вы после атрибута, '$ element -> att ('name');' должен делать это, как правило. (Я не думал, что '$ element -> name' будет, но если это произойдет, то это будет :)) – Sobrique

0

Я предпочитаю 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"; 
    } 
} 
+0

Увы, похоже, что XML :: LibXML не будет установлен на сервере, который я использую. –

+0

Это сервер Linux? Если это так, вы можете установить модуль XML :: LibXML из пакета ОС (например, 'apt-get install libxml-libxml-perl' на debian). Для Windows я считаю, что XML :: LibXML поставляется с популярными дистрибутивами Perl. –

+0

О, эй, @GrantMcLean! Я все еще помню ваше имя нежно 10 лет назад, когда я решил перейти к XML из текста с разделителями табуляции. Из-за этого я многому научился из вашего письма и пошел олл-ин с XML. Что касается вашего вопроса ... это старый сервер Linux. Много проблем с этим, вещи не будут установлены. Мне удалось установить Force TWIG, так что я работаю. Сервер вышел, я больше не работаю в компании, они просто попросили меня помочь получить данные из него для перехода на новую систему. –

1

ниже код должен дать вам достаточно информации, чтобы начать работу.

Несколько замечаний:

  • разобрать использование файла parsefile вместо parse
  • вы также можете использовать 'level(1)' вместо '/root/*'
  • с помощью закрытия для вызова обработчика (process_elt), передавая $atts и $strings - это чистый способ сделать это, если вы хотите, чтобы $atts и $strings были глобальными переменными, вы можете просто написать '/root/*' => \&process_elt и обработчик w плохой вызов с веточкой и элементом в качестве параметров
  • бит $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> 
Смежные вопросы