in reply to copy XML elements from one file to another

If you're mostly just copying the contents of several files, perhaps consider not using a module at all. Something along the lines of this code (following some Notes) might do what you want.

Notes:

#!/usr/bin/env perl -l use strict; use warnings; use Inline::Files; my %doc_fh_for = (File0 => \*FILE0, File1 => \*FILE1, File2 => \*FILE2 +); my $out_fh = \*STDOUT; my $key_file = 'File0'; my @feature_files = qw{File1 File2}; print $out_fh '<Key="1234">'; write_xml_content($key_file, $doc_fh_for{$key_file}, $out_fh, ' ' x 4) +; print $out_fh ' <Status="in use"/> <Features>'; for (@feature_files) { write_xml_content($_, $doc_fh_for{$_}, $out_fh, ' ' x 8); } print $out_fh ' </Features> <Other_Status/> </Key>'; sub write_xml_content { my ($file_id, $in_fh, $out_fh, $indent) = @_; while (<$in_fh>) { chomp; if (/^<\/?doc>$/) { s/doc/$file_id/; } else { if (/^<ABC id="(\d+)">$/) { my $id = $1; s/$id/$file_id-$id/; } $_ = ' ' x 4 . $_; } print $out_fh $indent, $_; } }

Inline::Files data:

Output:

<Key="1234"> <File0> <ABC id="File0-1"> <Feature> <Number>86839</Number> </Feature> </ABC> <ABC id="File0-2"> <Feature> <Number>82783</Number> </Feature> </ABC> </File0> <Status="in use"/> <Features> <File1> <ABC id="File1-1"> <Feature> <Number>86839</Number> </Feature> </ABC> <ABC id="File1-2"> <Feature> <Number>82783</Number> </Feature> </ABC> </File1> <File2> <ABC id="File2-1"> <Feature> <Number>86839</Number> </Feature> </ABC> <ABC id="File2-2"> <Feature> <Number>82783</Number> </Feature> </ABC> </File2> </Features> <Other_Status/> </Key>

-- Ken

Replies are listed 'Best First'.
Re^2: copy XML elements from one file to another
by Anonymous Monk on Nov 25, 2013 at 15:57 UTC

    well, you actually got exactly what im looking for. By taking a look at your code and the output example you provided, I can tell that you exactly understand what I am aiming at. Easy to track steps
    I'm working on it right now and I hope I can generate my desired output as you explained

Re^2: copy XML elements from one file to another
by Anonymous Monk on Nov 25, 2013 at 17:21 UTC

    After looking into this solution I realized the use of Inline::Files is only for small virtual files. But in my case eahc of my file is hundreds of lines.
    anyway this can be modified to read the 3 files at the same time and grab information by ID, instead of inline files ?
    plus im having issues install the Inline::File module...Thanks for your help

      The use of Inline::Files and \*STDOUT was simply for demo purposes; hence the "I'll leave you to replace Inline::Files and \*STDOUT with more appropriate I/O.".

      I'm not sure whether you've got a handle on this. If you have, you probably won't need the following; if not, here's some hints and tips that may prove useful for your real code. Ask if you need more information, further explanation, etc.

      I'd recommend the autodie pragma. This will save a lot of effort checking whether files could be opened, created, read, written and so on. Put it near the top of your code; I usually write this:

      ... use strict; use warnings; use autodie; ...

      Remove the 'use Inline::Files; line.

      Use open to create your filehandles.

      The output filehandle ($out_fh) is very straightforward:

      open my $out_fh, '>', '/path/to/output_file';

      The input filehandles are a little more complicated but still fairly easy. I wouldn't recommend creating all the filehandles in advance; instead, open and close one at a time. The \*FILE0, \*FILE1, etc. in my original code are filehandles for the Inline::Files: you'll see I didn't need to explicitly create those. Perhaps change:

      my %doc_fh_for = (File0 => \*FILE0, File1 => \*FILE1, File2 => \*FILE2 +);

      to something like this

      my %source_file_for = (File0 => 'filename0', File1 => 'filename1', Fil +e2 => 'filename2');

      and then further down

      for (@feature_files) { open my $in_fh, '<', $source_file_for{$_}; write_xml_content($_, $in_fh, $out_fh, ' ' x 8); close $in_fh; }

      I don't know whether you have a predetermined list of source files or if you have to find them in one or more directories; if the latter, you may find readdir useful for this. Also note that may need to prefix the filenames with directory paths: while simple concatenation may suffice, I'd normally use File::Spec as it's portable (it's also a core module so there's no need to install it).

      -- Ken

        Thanks Ken. I can see some resutls now however the following issues exists:

        (1)errors:
        readline<> on unopened filehandle at line33
        which is : while (<$in_fh>)

        (2)my FILE0 is being skipped and only FILE1 and FILE2 are being processed

        (3)Could you please give me quick explanation of whats going on within the subroutine write_xml_contents so that I can tweak my output as I desire.
        here is my code:

        #!/strawberry/perl/bin/perl #use strict; use warnings; use autodie; open my $out_fh, '>', 'out_data.txt'; my %source_file_for = (File0 => 'FILE0.xml', File1 => 'FILE1.xml', Fil +e2 => 'FILE2.xml'); #my $out_fh = \*STDOUT; my $key_file = 'File0'; my @feature_files = qw{File1 File2}; print $out_fh '<Key="1234">'; write_xml_content($key_file, $source_file_for{$key_file}, $out_fh, ' ' + x 4); print $out_fh "\n".' <Status="in use"/> <Features>'; for (@feature_files) { open my $in_fh, '<', $source_file_for{$_}; write_xml_content($_, $in_fh, $out_fh, ' ' x 8); close $in_fh; } print $out_fh "\n".' </Features> <Other_Status/> </Key>'; sub write_xml_content { my ($file_id, $in_fh, $out_fh, $indent) = @_; while (<$in_fh>) { chomp; if (/^<\/?doc>$/) { s/doc/$file_id/; } else { if (/^<ABC id="(\d+)">$/) { my $id = $1; s/$id/$file_id-$id/; } $_ = ' ' x 4 . $_; } print $out_fh $indent, $_; } }