use Data::Dumper; use strict; use warnings; my %projects = ( Projects => { "Project1" => { version => "3.41", status => "nul", components => { "Software" => { name => "controller", label => "RC_1.01", subComponents => { "Database" => { version => "1.01", }, "SerialComms" => { version => "2.13", } } }, "Firmware" => { name => "I/O Interface", label => "RC_1.21", } } }, "Project2" => { }, "Project3" => { } } ); for my $project_key (keys(%{$projects{"Projects"}})) { print "$project_key:\n"; my $project = $projects{"Projects"}->{$project_key};#this looks not necessary here, but in more complex code, it does help to simplify things if (defined($project->{"components"}{"Software"})) { for my $subcomponents_key (keys(%{$project->{"components"}{"Software"}{"subComponents"}})) { print $subcomponents_key, ":\n"; print Dumper($project->{"components"}{"Software"}{"subComponents"}{$subcomponents_key}); } } }