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; } #### [ { 'total' => '12.00', 'currency' => 'CAD', 'fee_lines' => '', 'created_at' => '2019-03-28 10:33:06', 'total_shipping' => '0.00', 'customer_id' => '1', 'order_number' => '359', 'total_discount' => '0.00', 'shipping_lines' => { 'method_id' => 'local_pickup', 'total' => '0.00', 'id' => '309', 'method_title' => 'Pickup' }, 'billing_address' => { 'phone' => '5065551212', 'city' => 'Moncton', 'province_code' => 'NB', 'country' => 'Canada', 'address_2' => '', 'last_name' => 'Blow', 'email' => 'someone@somewhere.com', 'country_code' => 'CA', 'province' => 'New Brunswick', 'postal_code' => 'E4E 4E4', 'address_1' => '123 Somestreet', 'first_name' => 'Joe', 'company_name' => '' }, 'shipping_address' => { 'province' => 'New Brunswick', 'country_code' => 'CA', 'postal_code' => 'E4E 4E4', 'address_1' => '123 Somestreet', 'first_name' => 'Joe', 'company_name' => '', 'city' => 'Moncton', 'province_code' => 'NB', 'country' => 'Canada', 'address_2' => '', 'last_name' => 'Blow' }, 'line_items' => [ { 'subtotal' => '10.00', 'total_tax' => '0.00', 'subtotal_tax' => '0.00', 'id' => '307', 'quantity' => '1', 'variation_id' => '0', 'tax_class' => '', 'sku' => '', 'total' => '10.00', 'product_thumbnail_url' => 'https://www.mysite.com/wp-content/uploads/2019/03/pepperoni-pizza-150x150.jpg', 'name' => 'Pepperoni Pizza', 'meta' => '', 'product_url' => 'https://www.mysite.com/product/pepperoni-pizza/', 'product_id' => '15', 'price' => '10.00' }, { 'total_tax' => '0.00', 'subtotal' => '2.00', 'quantity' => '2', 'variation_id' => '0', 'id' => '308', 'subtotal_tax' => '0.00', 'total' => '2.00', 'product_thumbnail_url' => 'https://www.mysite.com/wp-content/uploads/2019/03/pepsi-can-150x150.jpg', 'sku' => '', 'tax_class' => '', 'price' => '1.00', 'product_id' => '222', 'product_url' => 'https://www.mysite.com/product/pepsi/', 'meta' => '', 'name' => 'Pepsi' } ], } ]