如何在Perl中使用XML文件?

时间:2013-06-27 07:05:46

标签: xml perl xml-parsing

我需要使用Perl脚本在XML文件中获取具有给定名称的节点的子节点数据值。 我正在使用XML::LibXML::Simple

代码段如下所示:

my $booklist = XMLin(path);

  foreach my $book (@{$booklist->{detail}}) {
    print $book->{name} . "\n";
}

XML文件如下所示:

<?xml version='1.0' encoding='iso-8859-1'?>
<booklist>
<book>
<detail label='label1' status='active' type='none'>
<name>book1</name>
</detail >
<detail label='label2' status='active' type='none'>
<name>book2</name>
</detail >
</book>
</booklist>

当我使用上面的代码时,我收到以下错误消息: “不是ARRAY参考”

任何人都可以帮助我吗?

5 个答案:

答案 0 :(得分:2)

下面是在XML中使用的XML :: Simple的解决方案。

use strict;
use warnings;
use XML::Simple;

my $booklist = XMLin($ARGV[0], KeyAttr => [], ForceArray => qr/detail/);

foreach my $book (@{$booklist->{book}->{detail}}) {
    print $book->{name} . "\n";
}

这里重要的部分是给予XMLin的选项,强制将“细节”子节点表示为数组。

XML的简单快速入门:: Simple是关于CPAN的文档: http://metacpan.org/pod/XML::Simple

答案 1 :(得分:1)

当你写:

@{ $booklist->{detail} }

...你说$ booklist-&gt; {detail}返回一个数组引用,你希望perl将它取消引用到一个数组中,即'@'。

不要将<name>用作标记。 XML :: Simple很奇怪地解析它。这是一个例子:

1)

<?xml version='1.0' encoding='iso-8859-1'?>
<booklist>
  <book>
      <bname>book1</bname>
  </book>
  <book>
      <bname>book2</bname>
  </book>
</booklist>

use strict;   
use warnings;   
use 5.016;  

use XML::Simple;
use Data::Dumper;



my $booklist = XMLin('xml.xml');
print Dumper($booklist);


--output:--

$VAR1 = {
          'book' => [
                    {
                      'bname' => 'book1'
                    },
                    {
                      'bname' => 'book2'
                    }
                  ]
        };

2)现在看看使用<name>标签时会发生什么:

<?xml version='1.0' encoding='iso-8859-1'?>
<booklist>
  <book>
      <name>book1</bname>
  </book>
  <book>
      <name>book2</bname>
  </book>
</booklist>

--output:--
$VAR1 = {
          'book' => {
                    'book2' => {},
                    'book1' => {}
                  }
        };

所以用你原来的例子:

<?xml version='1.0' encoding='iso-8859-1'?>
<booklist>
  <book>

    <detail label='label1' status='active' type='none'>
      <bname>book1</bname>
    </detail>

    <detail label='label2' status='active' type='none'>
      <bname>book2</bname>
    </detail>

  </book>
</booklist>


--output:--
$VAR1 = {
          'book' => {
                    'detail' => [
                                {
                                  'bname' => 'book1',
                                  'status' => 'active',
                                  'label' => 'label1',
                                  'type' => 'none'
                                },
                                {
                                  'bname' => 'book2',
                                  'status' => 'active',
                                  'label' => 'label2',
                                  'type' => 'none'
                                }
                              ]
                  }
        };

要获取所有bname标记,您可以执行以下操作:

use strict;   
use warnings;   
use 5.016;  

use XML::Simple;
use Data::Dumper;

my $booklist = XMLin('xml.xml');
my $aref = $booklist->{book}{detail};

for my $href (@$aref) {
    say $href->{bname};
}


--output:--
book1
book2

答案 2 :(得分:1)

我想是这样......

use strict;
use XML::Twig;

my $text = join '', <DATA>;
my $story_file = XML::Twig->new(
                twig_handlers =>{
                'name' => \&name,
                keep_atts_order => 1,
},
                pretty_print => 'indented',
);
$story_file->parse($text);

sub name {
        my ($stroy_file, $name) = @_;
    print $name->text, "\n";
}

__END__
<?xml version='1.0' encoding='iso-8859-1'?>
<booklist>
<book>
<detail label='label1' status='active' type='none'>
<name>book1</name>
</detail >
<detail label='label2' status='active' type='none'>
<name>book2</name>
</detail >
</book>
</booklist>

答案 3 :(得分:1)

来自XML::Simple docs

  

不鼓励在新代码中使用此模块。其他模块可用,提供更直接和一致的接口。特别强烈建议使用XML :: LibXML。

     

此模块的主要问题是大量选项以及这些选项交互的任意方式 - 通常会产生意外结果。

反正。

在您的代码中,您忽略了书单包含包含详细信息的书籍这一事实。书单没有直接的细节。以下是使用XML::LibXML的简短解决方案:

use strict; use warnings; use 5.010; use XML::LibXML;

my $dom = XML::LibXML->load_xml(IO => \*DATA) or die "Can't load";

for my $detail ($dom->findnodes('/booklist/book/detail')) {
    say $detail->findvalue('./name');
}

__DATA__
<?xml version='1.0' encoding='iso-8859-1'?>
<booklist>
  <book>
    <detail label='label1' status='active' type='none'>
      <name>book1</name>
    </detail >
    <detail label='label2' status='active' type='none'>
      <name>book2</name>
    </detail >
  </book>
</booklist>

正如您在XPATH表达式/booklist/book/detail中所看到的,我们首先必须在查找详细信息之前查看本书。当然,这可以缩短为//detail

通常,如果数据结构不是它看起来的那样,你应该转储它,例如

use Data::Dumper;
print Dumper $booklist;

这将输出:

$VAR1 = {
  'book' => {
    'detail' => {
      'book2' => {
        'status' => 'active',
        'type' => 'none',
        'label' => 'label2'
      },
      'book1' => {
        'status' => 'active',
        'type' => 'none',
        'label' => 'label1'
      }
    }
  }
};

因此,出于某些原因,book1book2字符串现在是嵌套哈希中的。帮自己一个忙,并停止在CPAN上使用最复杂的XML模块,即“XML :: Simple”。

答案 4 :(得分:0)

使用XML::Rules的另一种方式(假设要点是'详细'而不是仅打印'name'的内容):

use XML::Rules;
my @rules = (
  detail => sub {
    print "$_[1]{name}\n";
    return;
  },
  name => 'content',
  _default => undef,
);

my $xr = XML::Rules->new(rules => \@rules);
$xr->parsefile("tmp.xml");