use Data::Dumper; use XML::LibXML; my $xml = q| 3593592019-03-28 10:33:06...etc...|; my @top_level = qw( order_number created_at total total_shipping total_discount customer_id currency ); my @nested = qw( shipping_lines billing_address shipping_address fee_lines ); my @orders; my $doc = XML::LibXML->load_xml(string => $xml); my @nodes = $doc->findnodes('//order'); for my $node (@nodes) { my %order; # get the top level nodes for my $name ( @top_level ) { $order{$name} = $node->findvalue($name); } # get the nested nodes for my $name ( @nested ) { my ($elem) = $node->findnodes($name); $order{$name} = process_nested($elem); } # special case for line_items node my ($line_items) = $node->findnodes('line_items'); $order{'line_items'} = process_line_items( $line_items ); push( @orders, \%order ); } print Dumper(\@orders); sub process_nested { my ($node) = @_; my %item; my $elem = $node->firstChild or return ''; $item{ $elem->nodeName } = $elem->textContent; while ( my $next = $elem->nextSibling ) { $item{ $next->nodeName } = $next->textContent; $elem = $next; } return \%item; } sub process_line_items { my ($node) = @_; my @items; my @id_nodes = $node->findnodes('id') or return ''; for my $id_node ( @id_nodes ) { my $elem = $id_node; my %item = ( id => $elem->textContent ); while ( my $next = $elem->nextSibling ) { my $name = $next->nodeName; # exit if we have reached the next item last if $name eq 'id'; $item{$name} = $next->textContent; $elem = $next; } push(@items, \%item); } return \@items; }