【问题标题】:Perl, XML::Twig, how to reading field with the same tagPerl,XML::Twig,如何读取具有相同标签的字段
【发布时间】:2014-07-29 04:53:23
【问题描述】:

我正在处理从合作伙伴那里收到的 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;    
}

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

谁能帮帮我?

谢谢

【问题讨论】:

    标签: xml perl xml-twig


    【解决方案1】:

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

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

    【讨论】:

    • 嗨 Choroba,谢谢,这行得通!我在 lcoalcosts 领域尝试了这个解决方案,但没有奏效。但这确实!超级棒!
    • 您的其他字段应该类似于localcost/typelocalcost/name 等。猜猜您使用的是localcosts/type?您可以使用localcosts/localcost/type,但没有必要
    【解决方案2】:

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

    此外,在处理localcost 元素时,处理程序执行了$twig->purge,从内存中删除数据。因此,当调用 localcosts 处理程序时,它会发现元素为空

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

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

    非常重要你在每个 Perl 程序的顶部use strictuse warnings,尤其是当你寻求帮助时。这是一个简单的测量方法,可以揭示许多直接的错误,否则您可能会浪费大量时间搜索

    另请注意,不鼓励使用“间接对象”形式的方法调用:您应该编写 XML::Twig-&gt;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
    

    【讨论】:

    • 嗨 Borodin,这看起来是一个非常好的解决方案。这将需要我一些时间来重写我拥有的模块,但我喜欢这个想法。它非常整洁。感谢您的帮助!
    • @user2970543:很高兴它对您有所帮助。您需要对更复杂的库(如 XML::Tiwg)进行大量工作,才能让特定情况下的最佳技术变得明显
    【解决方案3】:

    正如 Borodin 所说,如果您在 nametypedescription 上有处理程序,并且您在每个处理程序的末尾调用 $twig-&gt;purge,那么这些元素将从树中删除。你可以在object 上设置一个处理程序,它只调用$twig-&gt;purge,你会没事的。

    你不需要“太频繁”地调用purge,只要确保你调用它的级别足够低,这样你就不会占用太多内存。为每个单独的叶子元素调用它是没有意义的。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-06-01
      • 1970-01-01
      • 2013-06-01
      • 1970-01-01
      • 2016-12-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多