Perl, XML::Twig, как читать поле с тем же тегом

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

<?xml version="1.0" encoding="UTF-8"?>
<objects>
  <object>
    <id>VW-XJC9</id>
    <name>Name</name>
    <type>House</type>
    <description>
    <![CDATA[<p>some descrioption of the house</p>]]> </description>
    <localcosts>
      <localcost>
        <type>mandatory</type>
        <name>What kind of cost</name>
        <description>
          <![CDATA[Some text again, different than the first tag]]>
        </description>
      </localcost>
    </localcosts>
  </object>
</objects>

Причина, по которой я использую Twig, заключается в том, что этот XML имеет размер около 11 ГБ, около 100 000 различных объектов). Проблема в том, что когда я дохожу до части localcosts, 3 поля (тип, имя и описание) пропускаются, вероятно, потому, что эти имена уже использовались ранее.

Код, который я использую для просмотра XML-файла, выглядит следующим образом:

my $twig= new XML::Twig( twig_handlers => { 
                 id                            => \&get_ID,
                 name                          => \&get_Name,
                 type                          => \&get_Type,
                 description                   => \&get_Description,
                 localcosts                    => \&get_Localcosts
});

$lokaal="c:\\temp\\data3.xml";
getstore($xml, $lokaal);
$twig->parsefile("$lokaal");

sub get_ID          { my( $twig, $data)= @_;  $field[0]=$data->text; $twig->purge; } 
sub get_Name        { my( $twig, $data)= @_;  $field[1]=$data->text; $twig->purge; }
sub get_Type        { my( $twig, $data)= @_;  $field[3]=$data->text; $twig->purge; }
sub get_Description { my( $twig, $data)= @_;  $field[8]=$data->text; $twig->purge; }
sub get_Localcosts{

  my ($t, $item) = @_;

  my @localcosts = $item->children;
  for my $localcost ( @localcosts ) {
    print "$field[0]: $localcost->text\n";
    my @costs = $localcost->children;
    for my $cost (@costs) {
      $Type       =$cost->text if $cost->name eq q{type};
      $Name       =$cost->text if $cost->name eq q{name};
      $Description=$cost->text if $cost->name eq q{description};
      print "Fields: $Type, $Name, $Description\n";
    }
  }
  $t->purge;    
}

когда я запускаю этот код, основные поля читаются без проблем, но когда код достигает части «localcosts», второй цикл for-next не выполняется. Когда я меняю имена полей в xml на уникальные, этот код работает отлично.

Кто-нибудь может мне помочь?

Спасибо


person user2970543    schedule 08.06.2014    source источник


Ответы (3)


Если вы хотите, чтобы обработчики типа, имени и описания запускались только в теге объекта, укажите путь:

my $twig = new XML::Twig( twig_handlers => { 
                 id                    => \&get_ID,
                 'object/name'         => \&get_Name,
                 'object/type'         => \&get_Type,
                 'object/description'  => \&get_Description,
                 localcosts            => \&get_Localcosts
    });
person choroba    schedule 08.06.2014
comment
Привет Choroba, Спасибо, это работает! Я попробовал это решение с полем lcoalcosts, но это не сработало. Но это так! Супер! - person user2970543; 08.06.2014
comment
Другие ваши поля должны выглядеть как localcost/type, localcost/name и т. д. Как вы думаете, вы использовали localcosts/type? Вы могли бы использовать localcosts/localcost/type, но в этом нет необходимости - person Borodin; 08.06.2014

Проблема в том, что обработчики id, name, type и description выполняются для обоих вхождений. Вы обнаружите, что содержимое @fields взято из значений localcost, поскольку данные из значений object были перезаписаны.

Кроме того, при обработке элементов localcost обработчики выполнили $twig->purge, которая удаляет данные из памяти. Поэтому, когда вызывается обработчик localcosts, он находит элемент пустым.

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

Эта программа демонстрирует. Обратите внимание, что я использовал Data::Dumper только для того, чтобы вы могли видеть содержимое @fields после его заполнения.

очень важно, чтобы вы use strict и use warnings были в начале каждой программы Perl, особенно если вы обращаетесь за помощью. Это простая мера, которая может выявить множество простых ошибок, на поиск которых в противном случае вы могли бы потратить много времени.

Обратите также внимание на то, что форма вызова метода "косвенный объект" не рекомендуется: вы должны писать XML::Twig->new(...) вместо new XML::Twig (...).

И если вы используете одинарные кавычки вместо двойных, то обратную косую черту внутри строки не нужно удваивать, если только она не является последним символом строки. Но Perl вполне доволен, если вы используете косую черту в качестве разделителя пути, даже в Windows.

надеюсь, это поможет

use strict;
use warnings;

use XML::Twig;
use Data::Dumper;
$Data::Dumper::Useqq = 1;

my $twig= XML::Twig->new( twig_handlers => { object => \&get_Object });

my $lokaal = 'c:\temp\data3.xml';

my @fields;
$twig->parsefile($lokaal);


sub get_Object {

  my ($twig, $object) = @_;

  $fields[0] = $object->findvalue('id');
  $fields[1] = $object->findvalue('name');
  $fields[3] = $object->findvalue('type');
  $fields[8] = $object->findvalue('description');

  print Dumper \@fields;

  my @localcosts = $object->findnodes('localcosts/localcost');

  for my $localcost (@localcosts) {

    my $type        = $localcost->findvalue('type');
    my $name        = $localcost->findvalue('name');
    my $description = $localcost->findvalue('description');

    print "$type, $name, $description\n";
  }

  $twig->purge;    
}

вывод

$VAR1 = [
          "VW-XJC9",
          "Name",
          undef,
          "House",
          undef,
          undef,
          undef,
          undef,
          "<p>some descrioption of the house</p> "
        ];
mandatory, What kind of cost, Some text again, different than the first tag
person Borodin    schedule 08.06.2014
comment
Привет, Бородин, это выглядит как очень хорошее решение. Это займет у меня некоторое время, чтобы переписать модуль, который у меня есть, но мне нравится эта идея. Это очень аккуратно. Спасибо за вашу помощь! - person user2970543; 08.06.2014
comment
@ user2970543: Я рад, что это помогло вам. Вам нужно проделать много работы с более сложными библиотеками, такими как XML::Tiwg, прежде чем станет очевидным лучший метод для данной ситуации. - person Borodin; 08.06.2014

Как сказал Бородин, если у вас есть обработчики на name, type и description, и вы вызываете $twig->purge в конце каждого обработчика, то элементы удаляются из дерева. Вы можете установить обработчик на object, который выполняет только вызов $twig->purge, и все будет в порядке.

Вам не нужно вызывать purge "слишком часто", просто убедитесь, что вы вызываете его на достаточно низком уровне, чтобы не использовать слишком много памяти. На самом деле нет смысла вызывать его для каждого отдельного листового элемента.

Это распространенная ошибка, которую я совершаю довольно часто ;--(.

person mirod    schedule 08.06.2014