перебор элементов с использованием libxml в perl

I have an XML file like below,

<?xml version="1.0"?>
<data>
  <header>
    <name>V9 Red Indices</name>
    <version>9</version>
    <date>2017-03-16</date>
  </header>
  <index>
    <indexfamily>ITRAXX-Asian</indexfamily>
    <indexsubfamily>iTraxx Rest of Asia</indexsubfamily>                
    <paymentfrequency>3M</paymentfrequency>
    <recoveryrate>0.35</recoveryrate>
    <constituents>
      <constituent>
        <refentity>
          <originalconstituent>
            <referenceentity>ICICI Bank Limited</referenceentity>
            <redentitycode>Y1BDCC</redentitycode>
            <role>Issuer</role>
            <redpaircode>Y1BDCCAA9</redpaircode>
            <jurisdiction>India</jurisdiction>
            <tier>SNRFOR</tier>
            <pairiscurrent>false</pairiscurrent>
            <pairvalidfrom>2002-03-30</pairvalidfrom>
            <pairvalidto>2008-10-22</pairvalidto>
            <ticker>ICICIB</ticker>
            <ispreferred>false</ispreferred>
            <docclause>CR</docclause>
            <recorddate>2014-02-25</recorddate>
            <weight>0.0769</weight>
          </originalconstituent>
        </refentity>
        <refobligation>
          <type>Bond</type>
          <isconvert>false</isconvert>
          <isperp>false</isperp>
          <coupontype>Fixed</coupontype>
          <ccy>USD</ccy>
          <maturity>2008-10-22</maturity>
          <coupon>0.0475</coupon>
          <isin>XS0178885876</isin>
          <cusip>Y38575AQ2</cusip>
          <event>Matured</event>
          <obligationname>ICICIB 4.75 22Oct08</obligationname>
          <prospectusinfo>
            <issuers>                                                        
              <origissuersasperprosp>ICICI Bank Limited</origissuersasperprosp>
            </issuers>
          </prospectusinfo>
        </refobligation>
      </constituent>
    </constituents>
  </index>
</data>

Я хотел бы перебрать этот файл, не зная имен тегов. Моя конечная цель — создать хэш с именами и значениями тегов.

Я не хочу использовать findnodes с XPath для каждого узла. Это противоречит самой цели написания универсального загрузчика.

Я также использую XML-LibXML-2.0126, немного более старую версию.

Часть моего кода, использующая findnodes, приведена ниже. XML также был сокращен, чтобы избежать длинного запроса, которым он стал сейчас :)

use XML::LibXML;

my $xmldoc = $parser->parse_file( $fileName );
my $root = $xmldoc->getDocumentElement() || die( "Could not get Document Element \n" );

foreach my $index ( $root->findnodes( "index" ) ) {    # $root->getChildNodes()) # Get all the Indexes

    foreach my $constituent ( $index->findnodes( 'constituents/constituent' ) ) { # Lets pick up all Constituents

        my $referenceentity = $constituent->findnodes( 'refentity/originalconstituent/referenceentity' );    # This is a crude way. we should be iterating without knowing whats inside

        print "referenceentity :" . $referenceentity . "\n";
        print "+++++++++++++++++++++++++++++++++++ \n";
    }
}

person BRATVADDI    schedule 24.05.2017    source источник


Ответы (2)


Используйте методы nonBlankChildNodes, nodeName и textContent, предоставляемые XML::LibXML::Node< /а>:

my %hash;

for my $node ( $oc->nonBlankChildNodes ) {

    my $tag = $node->nodeName;
    my $value = $node->textContent;
    $hash{$tag} = $value;
}

Что эквивалентно:

my %hash = map { $_->nodeName, $_->textContent } $oc->nonBlankChildNodes;
person Zaid    schedule 24.05.2017
comment
Выглядит аккуратно.. Хотя я использую XML-LibXML-2.0126, и, похоже, он не поддерживает Node. Не уверен, что хочу участвовать в установке новой версии. есть ли альтернатива? - person BRATVADDI; 24.05.2017
comment
Это довольно свежая версия XML::LibXML, я уверен, что у вас будет XML::LibXML::Node. Что возвращает perl -MXML::LibXML::Node -e 1 в командной строке? - person Zaid; 24.05.2017
comment
он возвращает ошибку невозможности найти Не удается найти XML/LibXML/Node.pm в @INC (@INC содержит: /app/ac/local/lib/perl5 /app/ac/lib/perl5 /app/ localapps/perl/lib/sun4-solaris-64int - person BRATVADDI; 24.05.2017
comment
Вы получаете такое же сообщение с perl -MXML::LibXML -e 1? - person Zaid; 24.05.2017
comment
Нет. Кажется, все работает нормально. ошибок не выдает.. perl -MXML::LibXML::NodeLis -e 1 ; вроде тоже нормально..может глючит установка? - person BRATVADDI; 24.05.2017
comment
Хм, в таком случае даже findnodes не сработает, так как он предоставляется тем же пакетом. Я не понимаю, как можно было бы пройти документ без XML::LibXML::Node - person Zaid; 24.05.2017
comment
findnodes действительно работает :) Я использую его уже некоторое время. Не хотел писать findnodes для каждого отдельного элемента и, следовательно, этот вопрос. - person BRATVADDI; 24.05.2017
comment
Я думаю, нам нужно посмотреть, как вы его используете, чтобы понять, что здесь происходит. Возможно, его можно установить по отдельному пути, который не является частью @INC? - person Zaid; 24.05.2017
comment
Относительно Я использую XML-LibXML-2.0126, и это, похоже, не поддерживает Node., это совсем не так. Все версии XML::LibXML имеют ::Node. Это базовый класс каждого объекта DOM. (XML::LibXML::Node не имеет собственного файла, поэтому use XML::LibXML::Node; и подобные ему не работают. Он предоставляется use XML::LibXML;.) - person ikegami; 24.05.2017
comment
Путаница была при попытке найти XML::LibXML::Node. Решение работает нормально. Спасибо! - person BRATVADDI; 26.05.2017

Вы уверены, что хотите этого? Получить доступ к произвольным данным из проанализированного объекта XML::LibXML::Document так же просто, как и из вложенного хэша Perl. Он, безусловно, займет меньше места в памяти, чем эквивалентный объект, если это ваше намерение, но из вашего вопроса это не так.

Вы можете легко сделать это с помощью модуля XML::Parser, который вызывает обратный вызов каждый раз, когда "событие " встречается в XML-данных. В данном случае нас интересуют только открытый тег, закрывающий тег и текстовая строка.

В этом примере кода создается вложенный хэш из XML. Он умирает с соответствующим сообщением, если данные XML искажены (закрывающий тег не соответствует имени открывающего тега) или если какой-либо из элементов имеет один или несколько атрибутов, которые не могут быть представлены в этой структуре.

Я использовал Data::Dump для отображения результата

use strict;
use warnings 'all';

use XML::Parser;
use Data::Dump;

my $parser = XML::Parser->new(
    Style    => 'Debug',
    Handlers => {
        Start => \&handle_start,
        End   => \&handle_end,
        Char  => \&handle_char,
    },
);


my %data;
my @data_stack = ( \%data );
my @elem_stack;

$parser->parsefile( 'index.xml' );
dd \%data;


sub handle_start {
    my ($expat, $elem) = @_;

    my $data = $data_stack[-1]{$elem} = { };
    push @data_stack, $data;
    push @elem_stack, $elem;

    if ( @_ > 2 ) {
        my $xpath = join '', map "/$_", @elem_stack;
        die qq{Element at $xpath has attributes};
    }
}


sub handle_end {
    my ($expat, $elem) = @_;

    my $top_elem = pop @elem_stack;
    die qq{Bad XML structure $elem <=> $top_elem} unless $elem eq $top_elem;

    pop @data_stack;
}


sub handle_char {
    my ($expat, $str) = @_;

    return unless $str =~ /\S/;

    my $top_elem = $elem_stack[-1];

    $data_stack[-2]{$top_elem} = $str;
}

выход

{
    data => {
        header => {
            date => "2017-03-16",
            name => "V9 Red Indices",
            version => 9,
        },
        index  => {
            constituents => {
                constituent => {
                    refentity => {
                        originalconstituent => {
                            docclause       => "CR",
                            ispreferred     => "false",
                            jurisdiction    => "India",
                            pairiscurrent   => "false",
                            pairvalidfrom   => "2002-03-30",
                            pairvalidto     => "2008-10-22",
                            recorddate      => "2014-02-25",
                            redentitycode   => "Y1BDCC",
                            redpaircode     => "Y1BDCCAA9",
                            referenceentity => "ICICI Bank Limited",
                            role            => "Issuer",
                            ticker          => "ICICIB",
                            tier            => "SNRFOR",
                            weight          => 0.0769,
                        },
                    },
                    refobligation => {
                        ccy            => "USD",
                        coupon         => 0.0475,
                        coupontype     => "Fixed",
                        cusip          => "Y38575AQ2",
                        event          => "Matured",
                        isconvert      => "false",
                        isin           => "XS0178885876",
                        isperp         => "false",
                        maturity       => "2008-10-22",
                        obligationname => "ICICIB 4.75 22Oct08",
                        prospectusinfo => {
                            issuers => {
                                origissuersasperprosp => "ICICI Bank Limited"
                            },
                        },
                        type => "Bond",
                    },
                },
            },
            indexfamily      => "ITRAXX-Asian",
            indexsubfamily   => "iTraxx Rest of Asia",
            paymentfrequency => "3M",
            recoveryrate     => 0.35,
        },
    },
}
person Borodin    schedule 24.05.2017