Search code examples
xmlperlxml-simple

Perl not interpreting value in ticks, calling it undefined value


I am writing a subroutine that uses the XML::Simple module to pull out information from a config file.

The XML file has the following structure

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

The code works fine. It puts the XML data into a nested hash as expected. But when I want to isolate one server from the rest using the current system's hostname I run into issues.

I think this line

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

should insert the current machine's hostname into the last set of brackets. However when execute the code I get

Can't use an undefined value as a HASH reference

Solution

  • 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.