Perl,XML :: Twig,如何使用相同的标记读取字段

时间:2014-06-08 14:02:06

标签: xml 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大约11GB,大约有100000个不同的对象)。问题是当我到达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;    
}

当我运行此代码时,主要字段的读取没有问题,但是当代码到达“本地成本”时部分,不执行第二个for-next循环。当我将xml中的字段名称更改为唯一的名称时,此代码可以正常工作。

有人能帮助我吗?

由于

3 个答案:

答案 0 :(得分:4)

如果您希望仅在对象标记中触发类型,名称和描述的处理程序,请指定路径:

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
    });

答案 1 :(得分:4)

问题在于idnametypedescription处理程序正在执行两次。您会发现@fields的内容来自localcost值,因为object值中的数据已被覆盖。

此外,在处理localcost元素时,处理程序已完成$ twig-&gt;清除,从内存中删除数据。因此,当调用localcosts处理程序时,它会找到空元素

我认为最简单的方法是编写一个单个处理程序,一次处理每个object节点,然后清除它

该程序演示。请注意,我仅使用了Data::Dumper,以便您在填充后可以看到@fields的内容

非常重要每个 Perl程序顶部的use strictuse warnings,尤其是在您寻求帮助的情况下它。这是一个简单的措施,可以揭示许多直截了当的错误,否则您可能会浪费大量时间来搜索

另请注意&#34;间接对象&#34;不鼓励方法调用的形式:你应该写XML::Twig->new(...)而不是new XML::Twig (...)

如果使用单引号而不是双引号,则字符串内的反斜杠不需要加倍,除非它是字符串的最后一个字符。但是如果你使用正斜杠作为路径分隔符,即使在Windows上

,Perl也会非常高兴

我希望这会有所帮助

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

答案 2 :(得分:2)

正如Borodin所说,如果您在nametypedescription上有处理程序,并且在每个处理程序的末尾调用$twig->purge,则会删除这些元素从树上。您可以在object上设置一个只能进行$twig->purge调用的处理程序,您就可以了。

您不需要经常调用purge“,只需确保以足够低的级别调用它,这样就不会使用太多内存。对每个单独的叶元素调用它没有任何意义。

这是一个常见的错误,我经常犯这个错误; - (。