in reply to Update XML Values using two primary keys

As usually, TIMTOWTDI. For example, you can pre-hash the names from the CSV by the coordinates, then process the XML node by node picking the new name from the hash:
#!/usr/bin/perl use warnings; use strict; use feature qw{ say }; use Text::CSV_XS; use XML::LibXML; my %name; my $csv = 'Text::CSV_XS'->new({allow_whitespace => 1, auto_diag => 1, binary => 1}); open my $in, '<:encoding(UTF-8)', 'test.csv' or die $!; $csv->header($in); while (my $row = $csv->getline($in)) { $name{$row->[0]}{$row->[1]} = $row->[2]; } my $dom = 'XML::LibXML'->load_xml(location => 'test.osm'); for my $node ($dom->findnodes('/osm/node')) { my $tag = $node->findnodes('tag[@k="name"]')->[0]; if (my $name = $name{ $node->{lat} }{ $node->{lon} }) { $tag->{v} = $name; } else { warn "No name found for $tag->{v}: @$node{qw{ lat lon }}\n"; } } $dom->toFile('new.osm');

Or, you can load the XML into a DOM, then read the CSV line by line, searching the DOM for the corresponding node and renaming it:

#!/usr/bin/perl use warnings; use strict; use feature qw{ say }; use Text::CSV_XS; use XML::LibXML; my $dom = 'XML::LibXML'->load_xml(location => 'test.osm'); my $csv = 'Text::CSV_XS'->new({allow_whitespace => 1, auto_diag => 1, binary => 1}); open my $in, '<:encoding(UTF-8)', 'test.csv' or die $!; $csv->header($in); my $xpath_template = '/osm/node[@lat="%"][@lon="%"]'; while (my $row = $csv->getline($in)) { my $i = 0; my $xpath = $xpath_template =~ s/%/$row->[$i++]/gr; my $node = $dom->findnodes($xpath)->[0]; if ($node) { my $tag = $node->findnodes('tag[@k="name"]')->[0]; $tag->{v} = $row->[2]; } else { warn "@$row missing in XML\n"; } } $dom->toFile('new.osm');

map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]

Replies are listed 'Best First'.
Re^2: Update XML Values using two primary keys
by pratikpooja (Novice) on Jan 06, 2021 at 15:36 UTC
    Thank you very much. I did apply the code and it is working as expected.
Re^2: Update XML Values using two primary keys
by pratikpooja (Novice) on Jan 07, 2021 at 18:57 UTC

    What if there are some tag values which are in csv file and that needs to be added as new nodes in xml file with the maximum node id assigned. For e.g.-

    "latitude";"longitude";"name";"id" 59.58464;8.56069;A13_PRA_KEI;-1643794 59.58464;8.56069;A13_RAT_KEI;-1643795 59.59465;8.57070;A14_PQR_KEI;-1643796
    The first 2 rows have same lat and long value but the 2nd row value needs to be added in xml file as it has maximum node id value. Thank you.

      When you build the hash, you can store both values (name and id) and each time compare to the existing id (a hash of hashes). If the new id isn't larger then just discard that row. You can then delete items from the hash as you go through the XML and add any left in the hash at the end.


      🦛

        I have created a routine to read the CSV file as a hash but I don't get how to compare to the existing id. Here is my piece of code. Can you please help how to do the comparison? Thank you.

        #!/usr/bin/perl use strict; use warnings; use Text::CSV; use Data::Dumper qw(Dumper); my $file = 'test_data.csv'; csv_hash($file); sub csv_hash { my ($filename) = @_; my $csv = Text::CSV->new ({binary => 1,auto_diag => 1,sep_char => + ';'}); open(my $data, '<:encoding(utf8)', $filename) or die "Could not op +en '$filename' $!\n"; my $header = $csv->getline($data); $csv->column_names($header); while (my $row = $csv->getline_hr($data)) { print(Dumper $row); } close $data; }

        The HoH worked, Thank you. Since the HoH is not sorted so how can we compare the key of hashes with the node latitude and longitude and update the names. For e.g. what should be the condition in the if statement?

        my $inputfile = 'test_data.csv'; my $csv = Text::CSV->new ({binary => 1,auto_diag => 1,sep_char =>';'} +); open(my $data, '<:encoding(utf8)', $inputfile) or die "Could not open +'$inputfile' $!\n"; my $header = $csv->getline($data); $csv->column_names($header); my %HoH; while (my $row = $csv->getline_hr($data)) { my $key = "$row->{latitude}.$row->{longitude}"; # Compare next if exists $HoH{$key} && $row->{id} >= $HoH{$key}->{id}; # Set (or overwrite) $HoH{$key} = {name => $row->{name},id => $row->{id}}; } foreach my $key1 (keys %HoH) { print "'$key1'\n"; foreach my $key2 (keys %{$HoH{$key1}}) { print $key2=$HoH{$key1}{$key2},"\n"; } } my $myhash = \%HoH; my $dom = 'XML::LibXML'->load_xml(location => 'test.osm'); for my $node ($dom->findnodes('/osm/node')) { my $tag = $node->findnodes('tag[@k="name"]')->[0]; print $tag,"\n"; if () { } }