1

我正在编写一个使用XML::Simple模块从配置文件中提取信息的子程序。

XML 文件具有以下结构

<main>
  <server hostname="blahblah" ... more_attributes="more"/>
  <server etc./>
  <server etc./>
</main>

代码工作正常。它按预期将 XML 数据放入嵌套散列中。但是当我想使用当前系统将一台服务器与其他服务器隔离时,hostname我遇到了问题。

我认为这条线

my %systemHash = %{$xmlObject->{SERVER}->{`hostname`}};

应该将当前机器的主机名插入到最后一组括号中。但是,当执行我得到的代码时

Can't use an undefined value as a HASH reference
4

2 回答 2

5

首先,不要使用XML::Simple. 它自己的文档说明了这一点

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

这个模块的主要问题是大量的选项以及这些选项交互的任意方式——通常会产生意想不到的结果。

您还应该使用 . 检查任何子例程调用或 shell 命令的结果Data::Dump。它看起来像这样

perl -MData::Dump -E'dd `hostname`'

在我的系统上显示

"Samurai-U\n"

希望你现在看到问题了吗?反引号返回的字符串有一个尾随换行符,并且您的$xmlObject哈希中有一个元素具有这样的键。你可以用

chomp(my $host = `hostname`)

之后你可以写

my %systemHash = %{ $xmlObject->{SERVER}{$host} }

最后,像这里一样复制所有第一级哈希是很浪费的

my %systemHash = %{$xmlObject->{SERVER}->{`hostname`}}

您不会继续显示您想要使用此信息的目的,但一般来说提取哈希引用要好得多,使用

chomp( my $hostname = `hostname` );
my $systemHash = $xmlObject->{SERVER}{$hostname};

更新

使用适当的 XML 解析模块会好得多。

这是一个使用示例解决方案XML::LibXML

use strict;
use warnings;
use 5.010;    # For 'say'

use XML::LibXML;

my ($xml_file) = @ARGV;

my $xml = XML::LibXML->load_xml(location => $xml_file);

my @servers      = $xml->findnodes('/main/server');
my @server_names = map $_->findvalue('@hostname'), @servers;

say "- $_" for @server_names;

输入文件

<main>
  <server hostname="server1" more_attributes="more"/>
  <server hostname="server2" more_attributes="more"/>
  <server hostname="server3" more_attributes="more"/>
</main>

输出

- server1
- server2
- server3
于 2014-06-16T23:25:54.153 回答
3

You should use Data::Dumper to dump the output from XML::Simple like this:

use Data::Dumper;
# Retrieve your data structure via XML::Simple. Then...

print Dumper $xmlObject;

You will see that it's not creating the structure that you expect, which is why you're getting the Can't use an undefined value as a HASH reference message. That message means that either $xmlObject is undefined, or that $xmlObject->{SERVER} is undefined. perldiag describes the error as:

A value used as either a hard reference or a symbolic reference must be a defined value. This helps to delurk some insidious errors.

You are treating an undefined value as a hash reference. And as mentioned, cause is probably that the structure XML::Simple produced differs from your expectations.

XML::Simple isn't as simple as its name implies, nor as one would hope. And currently its own documentation discourages its use. There are configuration options which help to normalize the structure it produces, but without seeing more of your XML, and the code you're using to read it, that's about as detailed an explanation as I can give. The best advice with respect to how you're parsing your XML is to use a more reliable module. Two very common and well-regarded alternatives are XML::Twig and XML::LibXML.

There's an additional bug in your code, that you will encounter next: The hostname returned by your system call to 'hostname' has a newline at the end, so you're asking for a hash key named:

"somehost\n"

...when you really want just "somehost". I suspect you're not counting on that. Minimally, you'll need something like this:

chomp( my $h = `hostname` );
my %systemHash = %{$xmlObject->{SERVER}->{$h}};

Or this:

use Sys::Hostname;
my %systemHash = %{$xmlObject->{SERVER}->{hostname()};

I would prefer the latter, as it's more portable, and the module Sys::Hostname comes with Perl, so you already have it on your system. Its output will not require chomping.

于 2014-06-16T23:02:14.410 回答