my @columns = qw(parentNodeId nodeId level canode label); my $CSV = <<'*END*'; 0,1,0,blah,bloh 1,2,1,foo,faaa 1,3,1,bar,barrar 2,4,2,baz,bazbaz 2,5,2,aaa,aaaaa 3,6,2,bbb,bbbbbbab 4,7,3,zzz,zzzzz *END* my (%sections, $root); my @navmod = split /\n/, $CSV; foreach my $line (@navmod) { my %current; @current{@columns} = split /,/, $line; delete $current{level}; my $nodeId = $current{nodeId}; $sections{$nodeId} = \%current; my $parentNodeId = delete $current{parentNodeId}; if ($parentNodeId) { push @{$sections{$parentNodeId}{section}}, \%current; } else { $root = \%current; } } use XML::Rules; my $parser = XML::Rules->new( rules => []); print $parser->ToXML(section => $root, 0, ' ');