0

Hy, i want to write all files within a directory and all sub-directories to an associative array and create a XML from this aray. here is my code.

use strict;
use warnings;

use File::Find;
use Data::Dumper;
use XML::Simple;

my $base_dir = '.';

my $xs = new XML::Simple;
my %filelist;

find(\&print_name_if_dir, $base_dir);

sub print_name_if_dir
{
    if (-f $_) {
        $filelist{$File::Find::name}{size}=-s $_;
        $filelist{$File::Find::name}{time}=(stat $_)[9];
    }
}

print Dumper(\%filelist);
my $xml = $xs->XMLout(\%filelist, keyattr => "filename");
print $xml;

and this is my output:

$VAR1 = {
          './out.log' => {
                           'size' => 51,
                           'time' => 1345720984
                         },
          './foo/bat.txt' => {
                               'size' => 24,
                               'time' => 1345708287
                             },
          './test.pl' => {
                           'size' => 438,
                           'time' => 1345720762
                         }
        };
<opt>
  <./out.log size="51" time="1345720984" />
  <./foo/bat.txt size="24" time="1345708287" />
  <./test.pl size="438" time="1345720762" />
</opt>

the array is okay but the XML should look like:

<file name="bla.txt">
  <time>"1234"</time>
  <size>"1234"</size>
</file> 

please help me.

AndyH
  • 95
  • 1
  • 1
  • 5

1 Answers1

2

XML::Simple is, well, pretty simple. Its documentation even says so itself. You cannot get every conceivable output form from it, and yours is most likely not possible.

However, you can get closer than where you're at now with e.g. this storage structure:

my $files = {
  file => [
    { name => 'chunky.txt', time => 12345, size => 321 },
    { name => 'bacon.dat',  time => 47123, size => 222 },
  ],
};

This would result in the following output (via print XMLout($files)):

<opt>
  <file name="chunky.txt" size="321" time="12345" />
  <file name="bacon.dat" size="222" time="47123" />
</opt>

What is impossible, in my opinion, is forcing time and size to be output as children instead of attributes.

Oh, such a structure can easily be built with the following code:

my %filelist = ( file => [] );

sub print_name_if_dir {
  return unless -f;

  push @{ $filelist{file} }, {
    name => $_,
    size => -s $_,
    time => (stat $_)[9],
  };
}
Moritz Bunkus
  • 11,592
  • 3
  • 37
  • 49
  • thank you very much! do you know how i can search for a specific file name in this array without a loop and thenn access its attributes? – AndyH Aug 23 '12 at 11:59
  • 1
    Without a loop? You could simply store the hash refs created in `print_name_if_dir` in a second hash by file name as well (let's call it `%filerefs`). Add this as the last line in `print_name_if_dir`: `$filerefs{$_} = $filelist{file}->[-1];` – Moritz Bunkus Aug 23 '12 at 12:04