danieln
danieln

Reputation: 513

Convert the contents of directory and its subdirectories to JSON

I have the following perl script from this stack exchange question that converts the content of a directory into JSON.

use File::Find; 
use JSON; 
use strict; 
use warnings; 

my $dirs={}; 
my $encoder = JSON->new->ascii->pretty; 
find({wanted => \&process_dir, no_chdir => 1 }, "."); 

print $encoder->encode($dirs); 
sub process_dir { 
    return if !-d $File::Find::name; 
    my $ref=\%$dirs; 

    for(split(/\//, $File::Find::name)) { 
        $ref->{$_} = {} 
        if(!exists $ref->{$_}); 
        $ref = $ref->{$_}; 
  } 
}

I'm running the script using Termux on Android 6.0.

Consider the following directory:

.
 |--server .
 |         | - File 1.tmp
 |         | - File 2.sql
 |         | - File 3.sql
 |-- js .
 |         | - File 1.js
 |         | - File 2.js
 |         | - File 3.js
 | -css .
 |         | - File 1.js
 |         | - File 2.js
 |         | - File 3.js
 | -assets .
 |         | - Font-awesome 
 |         |        .
 |         |        | - webfont.
 |         |        |          | - File 2.woff
 |         |        |          | - File 2.woff
 |         |        |          | - File 3.woff
 |         |        | - css .
 |         |        |       | - File 2.css
 |         |        |       | - File 2.css
 |         |        |       | - File 3.css
 |         | - Fonts
 |         |        | - ps  .
 |         |        |       | - File 2.woff
 |         |        |       | - File 2.css
 |         |        |       | - File 3.txt
 |         | - images 
 |         |        | - File 1.png
 |         |        | - File 2.png
 |         |        |   - File 3.svg

The script returns the following if I execute it in the above directory:

{
"." : {
      "server" : {},                               
      "js" : {},
      "css" : {},
      "assets" : {
         "fonts" : {                                       
            "ps":{}
         },
         "images" : {},
         "font-awesome" : {
            "css" :{},                                     
            "webfonts" : {}
         }
      }                                 
     }                                            
    }

I'm trying to edit the script to additionally include the files in each subdirectory as a Js array (if it's possible using perl + JSON Module, or, just Perl)

Eg [Edit]

{
"." : {
      "server" : {"Files": ["File1", "File2", "File n"]},
      /* Or just "server" : ["File1", "File2", "File n"], ... */
      "js" : {"Files": ["File1", "File2", "File n"]},                                
      "assets" : {
         "fonts" : {                                       
             "ps":{"Files": ["File1", "File2", "File n"]}
          },
         "images" : {"Files": ["File1", "File2", "File n"]},
         "font-awesome" : {
         "css" : {"Files": ["File1", "File2", "File n"]}                    
         "webfonts" : {"Files": ["File1", "File2", "File n"]}
     }
  }     
 }                                            
}

Is this achievable? If so, how should I go about it? I'm relatively new to perl and I'm having a hard time wrapping my head around this.

Upvotes: 2

Views: 400

Answers (3)

Polar Bear
Polar Bear

Reputation: 6808

Following piece of code puts directory structure into hash and then converts it into JSON format

use strict;
use warnings;

use JSON;

my $data;

my $dir = shift // '.';   # // correct website highlighting

$data->{$dir} = traverse($dir);

my $encoder = JSON->new->ascii->pretty; 

print $encoder->encode($data); 

sub traverse {
    my $dir = shift;

    my($dh,$data);

    $data->{'files'} = ();

    opendir($dh, $dir)
        or die "Could not open $dir";

    while( my $name = readdir($dh) ) {
        next if $name eq '.' or $name eq '..';

        my $path = "$dir/$name";

        $data->{$name} = traverse($path) if -d $path;
        push @{$data->{'files'}}, $name  if -f $path;
    }

    closedir $dh;

    return $data;
}

Upvotes: 0

Polar Bear
Polar Bear

Reputation: 6808

Other variation of the code: files are array elements, directories are hash with key directory name and content as array, then converted to JSON format

use strict;
use warnings;

use JSON;

my $data;

my $dir = shift // '.';    # // correct website highlighting

$data->{$dir} = traverse($dir, $dir);

my $encoder = JSON->new->ascii->pretty; 

print $encoder->encode($data); 


sub traverse {
    my $dir  = shift;
    my $name = shift;

    my($dh,@data,%ret);

    opendir($dh, $dir)
        or die "Could not open $dir";


    while( my $name = readdir($dh) ) {
        next if $name eq '.' or $name eq '..';

        my $path = "$dir/$name";

        push @data, traverse($path,$name) if -d $path;
        push @data, $name                 if -f $path;
    }

    closedir $dh;

    return ( $name => \@data );
}

Upvotes: 0

clamp
clamp

Reputation: 3262

I gave it a try and decided to move the file names to an entry with the special key __files__. This will break if you have a directory named like that.

use File::Find; 
use JSON; 
use strict; 
use warnings;
use 5.12.0;
use Data::Dumper;
my $dirs={}; 
my $encoder = JSON->new->ascii->pretty; 
find({wanted => \&process_dir, no_chdir => 1 }, "."); 
print $encoder->encode($dirs); 
sub process_dir { 
    my $full_name = $File::Find::name;
    my $ref=$dirs; 
    if (-d $full_name ) {
        for (split(/\//, $full_name)) {  # only directories
            $ref->{$_} ||= {};  # create the next level if it does not exist
            $ref = $ref->{$_};  # move to the next level         
        }
    } else {
        for (split(/\//, $full_name)){  # n directories and 1 filename
            if (exists $ref->{$_}) {    # it's a directory
                $ref = $ref->{$_}
            } else {
                push @{$ref->{__files__}}, $_;  # it's a filename
                # $ref->{$_} = '_is_file_';
            }
        }
    }
}

Upvotes: 2

Related Questions