user1128792
user1128792

Reputation: 73

XML::Twig concatenate XML files but keep the outer nodes

I'm trying to concatenate two XML files together but keep the outer nodes. Below is an example I found which does this but because it works off defining a root node the other nodes are discarded.

I tried using twig_print_outside_roots => 1 but this does not work. I have tried other approaches but seem to get further away than the example so after hours of trying I'm reaching out.

Any help is much appreciated. I prefer to use XML::Twig because I'm already using it to do other XML-related tasks.

#!/usr/bin/perl

use strict;
use warnings;

use XML::Twig;

my $result_twig;

foreach my $file ( 'to_concat_1.xml', 'to_concat_2.xml' ) {

    my $current_twig = XML::Twig->new( twig_roots => { Content => 1 } )->parsefile( $file );

    if ( ! $result_twig ) {
        $result_twig = $current_twig;
    }
    else {
        $current_twig->root->move( last_child => $result_twig->root )->erase;
    }
}

$result_twig->print;

to_concat_1.xml

<Envelope>
  <Body>
    <ContentRS>
      <Success/>
      <Contents>
        <Content>
          <Name> Mike </Name>
          <Email> [email protected]</Email>
        </Content>
      </Contents>
    </ContentRS>
  </Body>
</Envelope>

to_concat_2.xml

<Envelope>
  <Body>
    <ContentRS>
      <Success/>
      <Contents>
        <Content>
          <Name> Mark </Name>
          <Email> [email protected]</Email>
        </Content>
      </Contents>
    </ContentRS>
  </Body>
</Envelope>

Output:

<Envelope>
  <Content>
    <Name> Mike </Name>
    <Email> [email protected]</Email>
  </Content>
  <Content>
    <Name> Mark </Name>
    <Email> [email protected]</Email>
  </Content>
</Envelope>

Expected Output:

<Envelope>
  <Body>
    <ContentRS>
      <Success/>
      <Contents>
        <Content>
          <Name> Mike </Name>
          <Email> [email protected]</Email>
        </Content>
        <Content>
          <Name> Mark </Name>
          <Email> [email protected]</Email>
        </Content>
      </Contents>
    </ContentRS>
  </Body>
</Envelope>

Upvotes: 1

Views: 184

Answers (1)

user1128792
user1128792

Reputation: 73

I figured it out, so I'm posting what I came up with in case it helps someone else.

use strict;
use warnings;

use XML::Twig;

my @files = ( 'file1.xml', 'file2.xml', 'file3.xml' );

my $masterFile = pop( @files );

my @content;

foreach my $file ( @files ) {

    XML::Twig->new(
        twig_handlers => {
            'Content' => sub { push @content, $_; $_->cut(); }
        }
    )->parsefile( $file );
}

my $twig = XML::Twig->new(
    pretty_print  => 'indented_a',
    twig_handlers => {
        'Contents' => sub {
            foreach my $content ( @content ) {
                $content->paste( last_child => $_ );
            }
        }
    }
)->parsefile( $masterFile );

$twig->print;

Upvotes: 1

Related Questions