Код ниже сделает это, используя XML :: Twig
. Он будет работать с более чем 2 документами и работать, даже если не все идентификаторы присутствуют в обоих документах.Он загрузит оба файла в память, хотя, если вы хотите работать с документами, размер которых слишком велик, чтобы поместиться в памяти, код будет немного сложнее.Строки будут в том же порядке, что и в первом документе, а затем во втором (для тех, которые появляются только во втором).
Поскольку он написан как тест, вы можете выполнить тестслучай более сложный, или добавьте больше тестов, что, вероятно, было бы хорошей идеей.
#!/usr/bin/perl
use strict;
use warnings;
use Test::More;
use XML::Twig;
# normally you would read the documents from file,
# but it's easier to write a self-contained test
my $d1='
<document>
<row>
<id>1</id>
<data_field1>aaaa</data_field1>
<data_field2>bbbb</data_field2>
</row>
</document>
';
my $d2='
<document>
<row>
<id>1</id>
<data_field3>cccc</data_field3>
</row>
</document>
';
my $merged=
'<document>
<row>
<id>1</id>
<data_field1>aaaa</data_field1>
<data_field2>bbbb</data_field2>
<data_field3>cccc</data_field3>
</row>
</document>
';
$merged=~ s{\n}{}g; # remove \n's,
# if you want the result indented, look at the pretty_print option
is( merged( $d1, $d2), $merged, 'one test to rule them all');
done_testing();
sub merged
{
my @docs= map { XML::Twig->new->parse( $_) } @_;
my $merged= XML::Twig->new->parse( '<document></document>');
my %row_id; # hash id => row_element
foreach my $doc (@docs)
{ foreach my $row ($doc->root->children( 'row'))
{ my $eid= $row->first_child( 'id');
my $id= $eid->text;
# if the row hasn't been created in the merged doc, do it
if( ! $row_id{$id})
{ $row_id{$id}= $merged->root->insert_new_elt( last_child => 'row');
$row_id{$id}->insert_new_elt( last_child => id => $id);
}
# move the data fields to the end of the row
foreach my $data_field ($eid->next_siblings)
{ $data_field->move( last_child => $row_id{$id}); }
}
}
return $merged->sprint;
}