robamcclellan
robamcclellan

Reputation: 87

In perl how do I loop through this structure?

I am creating an xml to pass to an API and the API returns this (data dumped):

    (
    "Data::Dump",
    {
      SiteDevices => {
        "device"  => {
                       1102   => { address => "1.2.3.4", riskfactor => "1.0", riskscore => "0.0" },
                       1136   => { address => "1.2.3.5", riskfactor => "1.0", riskscore => "0.0" },
                       20491  => { address => "1.2.3.6", riskfactor => "1.0", riskscore => "0.0" },
                       129644 => { address => "1.2.3.7", riskfactor => "1.0", riskscore => "0.0" },
                       129645 => { address => "1.2.3.8", riskfactor => "1.0", riskscore => "0.0" },
                       130408 => { address => "1.2.3.9", riskfactor => "1.0", riskscore => "0.0" },
                       135975 => { address => "1.2.3.10", riskfactor => "1.0", riskscore => "0.0" },
                       137642 => { address => "1.2.3.11", riskfactor => "1.0", riskscore => "0.0" },
                     },
        "site-id" => 27,
      },
      success => 1,
    },
)

I want to loop through and print the devices and IPs associated with them and I for the life of me can’t come up with any code to do it. What the heck am I missing?! I tried to loop through a hash, a hash of hashes, etc. Can never get it to work. If any of you have a second and can provide an answer so I can shake my head in shame that’d be awesome.

I have tried:

foreach my $key (keys %{ $output->{‘SiteDevices’}->{‘device’} }) {
         print $key 
         print $key->{‘address’} 
}

and

foreach my $key (keys %{ $output{‘SiteDevices’}{‘device’} }) {
         print $key 
         print $key{‘address’} 
}

but neither works.

Upvotes: 2

Views: 349

Answers (2)

Sobrique
Sobrique

Reputation: 53488

You mention this is parsed XML. It looks like you've parsed it with XML::Simple and that's just not a good idea.

Why not instead:

#!/usr/bin/env perl
use strict;
use warnings;

use XML::Twig; 

## get xml somehow here. parsefile if it's a file already. 
my $twig = XML::Twig -> new -> parse ( $your_xml );

foreach my $device ( $twig -> get_xpath('//devices' ) ) {
   print $device -> att('name'), " => "; 
   print $device -> att('address'),"\n";
}

The useful thing here is xpath - it's not a regular expression, but it's sort of similar - and it applies to XML. In this case //device says 'find a <device> node anywhere in the tree. (And then we extract attributes).

That may work for your scenario, but you can be more specific by specifying a longer path:

  • ./device - directly below the current node.
  • .//device - anywhere below the current node.
  • /root/SiteDevices/device - nodes specifically matching this 'tree'.

You can also use xpath to search on attributes:

  • .//device[@name="1136"] will find something with the appropriate value and attribute.

See XML Twig quick reference for some guides on how to do this.

Upvotes: 6

stevieb
stevieb

Reputation: 9296

I believe the following loop will do what you are trying to accomplish. It fetches each device from the tree, then extracts out its address, and prints both:

for my $device (keys %{ $output->{SiteDevices}{device} }){
    print "$device: $output->{SiteDevices}{device}{$device}{address}\n";
}

In your tests, you're trying to use the key name as a hash/hash reference, which won't work. You need to put the extracted key back into the hash to perform further extractions.

Output, based on the single record I extracted out of your question:

129644: 1.2.3.7
129645: 1.2.3.8
130408: 1.2.3.9
137642: 1.2.3.11
1136: 1.2.3.5
135975: 1.2.3.10
1102: 1.2.3.4
20491: 1.2.3.6

Upvotes: 2

Related Questions