【问题标题】:How to display descendants in XML with Perl's XML::Twig?如何使用 Perl 的 XML::Twig 在 XML 中显示后代?
【发布时间】:2015-10-15 18:32:45
【问题描述】:

我是 2 周大的 Perl 用户,我正在尝试解析一个 300 mb 的嵌套 XML 文件。所以请原谅我缺乏知识。该文件遵循以下类似的格式

<?xml version="1.0" encoding="UTF-8"?>
   <APP:Report xsi:schemaLocation="WWW" xmlns:xsi="WWW" xmlns:APP="WWW">
   <library>
    <elt>
     <Book>The book of pages</Book>
     <Snap></Snap>
     <Line1>The Beginning</Line1>
     <Line2>We ceased to exist</Line2>
     <Line3>Accept it</Line3>
     <Line4>Now we live</Line4>
     <Line5>We reject it</Line5>
     <Rating>
      <C1>6.1</C1>
      <C2>8.9</C2>
      <C3>9.4</C3>
     </Rating>
    </elt>
    <Author>Sally</Author>
    <Publisher>Penguin</Publisher>
    <elt>
     <Book>The song</Book>
     <Snap></Snap>
     <Line1>This is how we do it</Line1>
     <Line2>I hope this works</Line2>
     <Line3>Please do</Line3>
     <Line4>Begging you</Line4>
     <Line5>Bye</Line5>
     <Rating>
      <C1>2.3</C1>
      <C2>9.9</C2>
      <C3>4.5</C3>
     </Rating>
    </elt>
    <Author>Justin</Author>
    <Publisher>Victoria</Publisher>
   </library>
  </APP:Report>

我希望能够在第一行的不同列中显示 Book、Snap、Line1、Line2、line3、Line4、line5、C1、C2 和 C3,在第 2 行中显示 Author,在第 3 行中显示 Publisher。这个只是我拥有的大文件的一个示例。我不想访问要显示的特定子项。我希望能够显示它的所有后代。

目前它正在打印我的所有数据第 1 行第 1 列。我的代码 sn-p 附在下面。最好的方法是什么?我将不胜感激任何建议。谢谢!

    my $twig= new XML::Twig();
$twig->parsefile( $_);    # build the twig 
  foreach my $elt ($twig->root->children)
  {
  print $fout1 $elt->text."\n";
}

编辑了问题:如果我在嵌套的孩子中有嵌套的孩子怎么办?最有效的做法是什么?例如,如何访问每个 C 的 elt 元素?我的第二个问题是关于如何显示这些元素,例如

  The book of pages|Snap|Line1|Line2|Line3|Line4|Line5|C1.X|
  The book of pages|Snap|Line1|Line2|Line3|Line4|Line5|C1.Y|
  The book of pages|Snap|Line1|Line2|Line3|Line4|Line5|C2.X|
  The book of pages|Snap|Line1|Line2|Line3|Line4|Line5|C2.Y|
  The book of pages|Snap|Line1|Line2|Line3|Line4|Line5|C3.X|
  The book of pages|Snap|Line1|Line2|Line3|Line4|Line5|C3.Y|
  .
  .
  .
  .
  .
  The song|Snap|Line1|Line2|Line3|Line4|Line5|C2.X|
  The song|Snap|Line1|Line2|Line3|Line4|Line5|C2.Y|
  Example 
    <Rating>
      <C1>
        <elt>
         <X></X>
         <X></X>
         </elt>
        <elt>
        <elt>
      </C1>
      <C2>
        <elt>
        <elt>
        <elt>
      </C2>
      <C3>
        <elt>
        <elt>
        <elt>
      </C3>
     </Rating>

就像 ikegami 建议的那样,最简单的方法是创建一个评级处理程序。但问题是解析它所花费的时间。我要解析的文件是 300 mb,并且有大约 20 个这样的例程,例如评级。所以我把大套路解析一次,然后把大套路的一部分解析20次。还有另一种方法可以做到这一点吗?还有比 XML::Twig 更有帮助的 XML 模块吗?

【问题讨论】:

    标签: xml perl parsing children


    【解决方案1】:

    所以你希望节点匹配 XPath

    descendant:*[count(*)=0]
    

    又名

    .//*[count(*)=0]
    

    相对于elt 元素。我使用 XML::LibXML,所以我会这样做

    $elt_node->findnodes("descendant:*[count(*)=0]")
    

    使用 XML::Twig 应该可以实现类似的解决方案。 (它确实有findnodes。)


    ug,我忘记了 XML::Twig 对 XPath 的支持有多糟糕。它不知道count,并且* 匹配非元素。没问题,我们只需要自己完成工作。

    use strict;
    use warnings;
    use feature qw( say );
    
    use XML::Twig qw( );
    
    my @eles = qw( Book Snap Line1 Line2 Line3 Line4 Line5 C1 C2 C3 );
    
    my $twig = XML::Twig->new(
       twig_handlers => {
          '/APP:Report/library/elt' => sub {
             my ($twig, $ele) = @_;
    
             my %row =
                map { $_->name() => $_->text() // '' }
                   # $ele->findnodes("descendant:*[count(*)=0]")
                   grep { $_->name() ne '#PCDATA' && ( grep { $_->name() ne '#PCDATA' } $_->children ) == 0 }
                      $ele->descendants();
    
             say join '|', @row{@eles};
    
             $twig->purge();  # Free unneeded memory.
          },
       },
    );
    
    say join '|', @eles;    
    $twig->parsefile('my_big.xml');
    

    输出:

    Book|Snap|Line1|Line2|Line3|Line4|Line5|C1|C2|C3
    The book of pages||The Beginning|We ceased to exist|Accept it|Now we live|We reject it|6.1|8.9|9.4
    The song||This is how we do it|I hope this works|Please do|Begging you|Bye|2.3|9.9|4.5
    

    【讨论】:

    • 我很惊讶这样做的效率!哇。谢谢!是否有另一种方法可以在不使用 twig_handlers 的情况下做到这一点,因为我还有需要解析的文档的其他部分?
    • 使用 XML::Twig 的重点在于通过twig_handlers支持大文件
    • 如果我必须解析整个文件,您不会建议使用 XML::Twig 吗?我还有一个问题。 $_->name() ne '#PCDATA' 是做什么的?
    • 还不错。非常慢,并且 cimmolèrent 错误处理命名空间,但代码并不算太糟糕,因为在这个答案中遇到了明显的异常。 #PCDATA 为文本节点返回
    • 谢谢!我稍微编辑了我的问题。你能帮我解决这个问题吗?如何修改我的 #row 以显示此内容?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-19
    • 2017-02-07
    • 1970-01-01
    • 1970-01-01
    • 2011-04-22
    相关资源
    最近更新 更多