如何将两个XML文件合并为一个?

时间:2012-02-13 20:25:47

标签: ruby perl

我需要合并两个XML文件。我之前看过这个问题,但那张海报想简单地连接这两个文件。我想根据特定的子元素进行合并,在本例中为id

我有两个XML文件,它们具有以下结构:

文件#1:

<document>
  <row>
      <id>1</id>
      <data_field1>aaaa</data_field1>
      <data_field2>bbbb</data_field2>
   </row>
</document>

文件#2:

<document>
  <row>
      <id>1</id>
      <data_field3>cccc</data_field3>
   </row>
</document>

我希望将它们合并到文件#3中:

<document>
  <row>
      <id>1</id>
      <data_field1>aaaa</data_field1>
      <data_field2>bbbb</data_field2>
      <data_field3>cccc</data_field3>
   </row>
</document>

使用id元素连接每个XML条目的位置。

1 个答案:

答案 0 :(得分:1)

下面的代码将使用XML :: Twig

执行此操作

它可以使用2个以上的文档,即使并非所有的ID都存在于两个文档中,它也能正常工作。它会将两个文件加载到内存中,如果您希望能够使用太大而无法放入内存的文档,则代码会更复杂一些。行将与第一个文档中的顺序相同,然后在第二个文档中(对于仅出现在第二个文档中的行)。

由于它是作为测试编写的,因此您可以使测试用例更复杂,或添加更多测试,这可能是一个好主意。

#!/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;
  }