Как получить список атрибутов узла XML с помощью XML::LibXML?

Учитывая следующий фрагмент XML:

<outline>
  <node1 attribute1="value1" attribute2="value2">
    text1
  </node1>
</outline>

Как мне получить этот вывод?

outline
node1=text1
node1 attribute1=value1
node1 attribute2=value2

Я просмотрел use XML::LibXML::Reader;, но этот модуль, похоже, предоставляет доступ только к значениям атрибутов, на которые ссылаются их имена. И как получить список имен атрибутов в первую очередь?


person Alexander Shcheblikin    schedule 07.11.2014    source источник


Ответы (2)


Вы найдете список атрибутов, выполнив $e->findnodes( "./@*");

Ниже приведено решение с простым XML::LibXML, а не XML::LibXML::Reader, которое работает с вашими тестовыми данными. Однако он может быть чувствителен к дополнительным пробелам и смешанному содержимому, поэтому перед использованием проверьте его на реальных данных.

#!/usr/bin/perl

use strict;
use warnings;

use XML::LibXML;

my $dom= XML::LibXML->load_xml( IO => \*DATA);
my $e= $dom->findnodes( "//*");

foreach my $e (@$e)
  { print $e->nodeName;

    # text needs to be trimmed or line returns show up in the output
    my $text= $e->textContent;
    $text=~s{^\s*}{};
    $text=~s{\s*$}{};

    if( ! $e->getChildrenByTagName( '*') && $text)
      { print "=$text"; }
    print "\n"; 

    my @attrs= $e->findnodes( "./@*");
    # or, as suggested by Borodin below, $e->attributes

    foreach my $attr (@attrs)
      { print $e->nodeName, " ", $attr->nodeName. "=", $attr->value, "\n"; }
  }
__END__
<outline>
  <node1 attribute1="value1" attribute2="value2">
    text1
  </node1>
</outline>
person mirod    schedule 07.11.2014
comment
Есть гораздо более чистые способы получения атрибутов. Очевидным является my @attrs = $e->attributes, который возвращает список всех узлов атрибутов, но объект узла элемента также ведет себя как связанная хэш-ссылка, и keys %$e возвращает все имена атрибутов, а $e->{attr_name} возвращает значение атрибута attr_name. - person Borodin; 07.11.2014
comment
спасибо, я не нашел этого в документах, что мне показалось странным. И теперь я вижу это в разделе «Перегрузка», да! Я до сих пор не вижу attributes, по крайней мере, в документах для XML::LibXML::Element - person mirod; 07.11.2014
comment
Понятно, я не ожидал найти его там. На самом деле в этом нет никакого смысла. Я вижу, что он также используется для возврата списка объявлений пространств имен, связанных с узлом, WTF? Почему 1 метод для 2 совершенно разных результатов? Я даже не могу найти его в спецификации DOM... Мальчик, я рад, что использую XML::Twig ;--) - person mirod; 07.11.2014
comment
Граница между XML::LibXML::Element и XML::LibXML::Node немного странная. Я бы ожидал, что все атрибуты появятся в первом, поскольку никакой другой тип узла не может иметь атрибутов. Но с объявлениями пространств имен все в порядке: пространство имен выглядит точно так же, как атрибут с именем xmlns. - person Borodin; 07.11.2014
comment
согласен, действительно с findnodes( "./@*") (или с использованием %$e) вы не получаете объявления пространств имен, а attributes дает их вам. И перед тестированием я думал, что attributes вернет список всех объявлений пространств имен, которые применялись к узлу, а не только те, которые объявлены в начальном теге элемента. - person mirod; 07.11.2014
comment
В моем списке того, что нужно сделать — ближе к концу, в разделе, отмеченном как интересное, — изучить и понять libxml2. библиотека, на которой это основано: подобные упражнения всегда улучшают мое понимание связанного программного обеспечения. Я надеюсь обнаружить, что такие странности, как эта, в библиотеке клея Perl в основном связаны с тем, что наше зрение проталкивается через толстые линзы очков автора. - person Borodin; 07.11.2014
comment
Большое спасибо! Мне нравятся оба решения: Бородина за использование attributes и мирода за унификацию подхода к узлам, ходящим с findnodes( "//*"). (Извините, мой вопрос был плохо составлен, <outline> в основном обычный узел, точно так же, как <node1>, поэтому мне действительно нужно было рекурсивно пройтись по всему документу.) Вы также проделали хорошую работу по разъяснению документации Perl; ) - person Alexander Shcheblikin; 08.11.2014

Что-то вроде этого должно вам помочь.

Из вашего вопроса неясно, является ли <outline> корневым элементом данных или он скрыт где-то в более крупном документе. Также неясно, насколько общим вы хотите, чтобы решение было - например. Вы хотите, чтобы весь документ был сброшен таким образом?

В любом случае, эта программа генерирует запрошенный вами вывод из данного XML-входа в довольно сжатой форме.

use strict;
use warnings;
use 5.014;     #' For /r non-destructive substitution mode

use XML::LibXML;

my $xml = XML::LibXML->load_xml(IO => \*DATA);

my ($node) = $xml->findnodes('//outline');

print $node->nodeName, "\n";

for my $child ($node->getChildrenByTagName('*')) {
  my $name = $child->nodeName;

  printf "%s=%s\n", $name, $child->textContent =~ s/\A\s+|\s+\z//gr;

  for my $attr ($child->attributes) {
    printf "%s %s=%s\n", $name, $attr->getName, $attr->getValue;
  }
}

__DATA__
<outline>
  <node1 attribute1="value1" attribute2="value2">
    text1
  </node1>
</outline>

вывод

outline
node1=text1
node1 attribute1=value1
node1 attribute2=value2
person Borodin    schedule 07.11.2014