0

I am new to Perl. I am trying to loop for each entry in xml file to perform a task but it's dying with "not an array reference at line 9".

My script is

#!/usr/bin/perl
# use module
use XML::Simple;
# create object
$xml = new XML::Simple;
# read XML file
$data = $xml->XMLin("data.xml");
# access XML data
foreach my $entry (@{$data->{entry}}){
print $entry->{'source-translation'}->{'static-ip'}-{'translated-address'};
}

My XML file data is

<?xml version="1.0"?>
<config version="6.0.0" urldb="paloaltonetworks">
  <entry name="DROP NAT">
    <source-translation>
      <static-ip>
        <bi-directional>yes</bi-directional>
        <translated-address>192.91.75.129</translated-address>
      </static-ip>
    </source-translation>
    <to>
      <member>Outside</member>
    </to>
    <from>
      <member>DROP</member>
    </from>
    <source>
      <member>192.91.66.72</member>
    </source>
    <destination>
      <member>any</member>
    </destination>
    <service>any</service>
    <description>NAT for DROP FTP Server</description>
    <to-interface>ethernet1/1</to-interface>
    <nat-type>ipv4</nat-type>
  </entry>
  <entry name="SDROP NAT">
    <source-translation>
      <static-ip>
        <bi-directional>yes</bi-directional>
        <translated-address>192.91.75.154</translated-address>
      </static-ip>
    </source-translation>
    <to>
      <member>Outside</member>
    </to>
    <from>
      <member>DROP</member>
    </from>
    <source>
      <member>192.91.66.79</member>
    </source>
    <destination>
      <member>any</member>
    </destination>
    <service>any</service>
    <description>NAT for SFTP server SDROP</description>
    <to-interface>ethernet1/1</to-interface>
    <nat-type>ipv4</nat-type>
  </entry>
</config>

I want to list translated-address in each entry. Assume there are lot of entries like this. Please help me.

3
  • 1
    use Data::Dumper; print Dumper $data; Commented Dec 16, 2015 at 9:43
  • 4
    Your script has only 11 lines. Which one is line 13? Commented Dec 16, 2015 at 9:47
  • 2
    Note: there is a minus sign in front of 'translated-address' probably you meant ->? Commented Dec 16, 2015 at 10:57

3 Answers 3

1

The default value for the KeyAttr option is

KeyAttr => [qw( name key id )]

That means that XML::Simple will convert

 [ { name => $name1, ... }, { name => $name2, ... }, ... ]

into

 { $name1 => { ... }, $name2 => { ... }, ... }

You could use the following option to the get the structure you expect.

KeyAttr => []

Don't forget to also provide the following option if you want your code to work when only one entry is present:

ForceArray => [qw( entry )]

The use of XML::Simple is discouraged, even by its own documentation.


XML::LibXML solution:

use XML::LibXML qw( );
my $parser = XML::LibXML->new();
my $doc = $parser->parse_file('data.xml');
for my $node ($doc->findnodes(
   '/config/entry/source-translation/static-ip/translated-address'
)) {
   print($node->textContent(), "\n");
}
Sign up to request clarification or add additional context in comments.

Comments

0

As you stated $data->{entry} is not an array reference in your data structure; it is a hash reference.

Check the script below, it accesses the data in the hash:

#!/usr/bin/perl
use v5.10; use strict; use warnings;
use XML::Simple;

my $xml = new XML::Simple;
my $data = $xml->XMLin("data.xml");
for my $entry (keys %{$data->{entry}}){
  say $data->{entry}->{$entry}->{'source-translation'}->{'static-ip'}->{'translated-address'};
}

And as Сухой27 pointed out; if you'd use Data::Dumper you'd be able to see the data structure and you'd see the hash instead of your assumed array.

Comments

0

Here is your problem:

use XML::Simple;

XML::Simple misleads you. It isn't simple at all. Unless you have absolutely no choice (and you probably do - it isn't core) then using another parser gives less pain.

Like:

use XML::Twig;
print $_ -> trimmed_text,"\n" for  XML::Twig -> new -> parsefile ( 'data.xml') -> get_xpath ( '//translated-address');

expanded out that's more like:

use strict ;
use warnings;
my $twig = XML::Twig -> new;
$twig -> parsefile ( 'data.xml');
foreach my $entry ( $twig -> findnodes ('//entry'))  {
    print $entry -> first_child('source-translation') -> first_child('static-ip') -> first_child_text('translated-address');
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.