The Perl and Raku Conference 2025: Greenville, South Carolina - June 27-29 Learn more

#!/usr/bin/env perl
# Copyright (c) 2015 Christian Jaeger, copying@christianjaeger.ch
# This is free software. See the file COPYING.md that came bundled
# with this file.
use strict;
use warnings FATAL => 'uninitialized';
# find modules from functional-perl working directory (not installed)
use Cwd 'abs_path';
our ($mydir, $myname);
BEGIN {
my $location = (-l $0) ? abs_path($0) : $0;
$location =~ /(.*?)([^\/]+?)_?\z/s or die "?";
($mydir, $myname) = ($1, $2);
}
use lib "$mydir/../lib";
sub usage {
print "usage: $myname [-o xmlfile] csvfile(s)
Open and read csvfile containing CSV with 4 columns, and write it to
xmlfile or stdout as XML. Does streaming, i.e. only loads one row
(plus some buffering) into memory at a time.
";
exit(@_ ? 1 : 0);
}
our $verbose = 0;
our $outpath;
GetOptions(
"verbose" => \$verbose,
"help" => sub {usage},
"outpath=s" => \$outpath,
) or exit 1;
usage unless @ARGV;
use FP::Text::CSV qw(csv_file_to_rows);
# create tag functions for the following XML tag names. Casing is
# preserved for the output, but the tag functions are all-uppercase
# (to try to avoid name conflicts and for better visibility) and
# replace the minus with the underscore.
qw(myexample
protocol-version
records
record
a
b
c
d);
use PXML::Serialize qw(pxml_print);
sub print_csv_as_xml {
my ($fh, $path) = @_;
my $rows = csv_file_to_rows $path, +{ eol => "\n", sep_char => ";" };
# $rows is a lazily-computed linked list of the rows
# skip the header row from the CSV file:
$rows = rest $rows;
# create a partially lazily computed PXML data structure
my $xmlstream = MYEXAMPLE(
PROTOCOL_VERSION("0.123"),
RECORDS # here we embed the lazily computed part:
(
$rows->map (
sub {
my ($row) = @_;
@$row == 4 or die "row doesn't contain 4 columns: @$row";
my ($a, $b, $c, $d) = @$row;
RECORD(A($a), B($b), C($c), D($d))
}
)
)
);
# walk and print the PXML data structure, forcing its evaluation
# as needed
pxml_print $xmlstream, $fh;
}
use Chj::xopen qw(glob_to_fh);
my $fh = defined $outpath ? xtmpfile $outpath : glob_to_fh * STDOUT;
print_csv_as_xml $fh, $_ for @ARGV;
$fh->xclose;
$fh->xputback(0666 & ~umask) if defined $outpath;