use strict;
use autodie;
use feature qw(say);
use List::Util qw(any);
use YAML::XS qw(LoadFile DumpFile);
$YAML::XS::Boolean = 'JSON::PP'; # use JSON::PP::Boolean objects
use Sort::Naturally qw(nsort);
use Exporter 'import';
our @EXPORT = qw(read_json read_yaml io_yaml_or_json write_json write_yaml);
#########################
#########################
# SUBROUTINES FOR I/O #
#########################
#########################
sub read_json {
my $str = path(shift)->slurp_utf8;
return decode_json($str); # Decode to Perl data structure
}
sub read_yaml {
my $data = LoadFile(shift); # Decode to Perl data structure
traverse_yaml_data_to_coerce_numbers($data)
; # revert floatings getting stringified by YAML::XS
return $data;
}
sub io_yaml_or_json {
my $arg = shift;
my $file = $arg->{filepath};
my $mode = $arg->{mode};
my $data = $mode eq 'write' ? $arg->{data} : undef;
# Checking for the below extension
my @exts = qw(.yaml .yml .json .jsonld .ymlld .yamlld);
my $msg = qq(Can't recognize <$file> extension. Extensions allowed are: )
. ( join ',', @exts ) . "\n";
my ( undef, undef, $ext ) = fileparse( $file, @exts );
die $msg unless any { $_ eq $ext } @exts;
# To simplify return values, we create a hash
$ext =~ tr/a.//d; # Unify $ext (delete 'a' and '.')
$ext =~ s/ld$//; # delete ending ld
my $return = {
read => { json => \&read_json, yml => \&read_yaml },
write => { json => \&write_json, yml => \&write_yaml }
};
# We return according to the mode (read or write) and format
return $mode eq 'read'
? $return->{$mode}{$ext}->($file)
: $return->{$mode}{$ext}->( { filepath => $file, data => $data } );
}
sub write_json {
my $arg = shift;
my $file = $arg->{filepath};
my $json_data = $arg->{data};
my $json =
JSON::XS->new->utf8->canonical->pretty->encode($json_data); # utf-8
path($file)->spew($json); # already need utf-8
return 1;
}
sub write_yaml {
my $arg = shift;
my $file = $arg->{filepath};
my $json_data = $arg->{data};
DumpFile( $file, $json_data );
return 1;
}
sub traverse_yaml_data_to_coerce_numbers {
my $data = shift;
# Traversing the data to force numbers to be numbers
# NB: Changing the original data structure
my $walker = Data::Leaf::Walker->new($data);
while ( my ( $key_path, $value ) = $walker->each ) {
$walker->store( $key_path, $value + 0 )
if Scalar::Util::looks_like_number $value;
}
}
1;