Revision f9f7b56e
Von Sven Schöling vor etwa 14 Jahren hinzugefügt
SL/Helper/Csv.pm | ||
---|---|---|
use Text::CSV;
|
||
use Params::Validate qw(:all);
|
||
use Rose::Object::MakeMethods::Generic scalar => [ qw(
|
||
file encoding sep_char quote_char escape_char header header_acc class
|
||
file encoding sep_char quote_char escape_char header dispatch class
|
||
numberformat dateformat _io _csv _objects _parsed _data _errors
|
||
) ];
|
||
|
||
... | ... | |
quote_char => { default => '"' },
|
||
escape_char => { default => '"' },
|
||
header => { type => ARRAYREF, optional => 1 },
|
||
header_acc => { type => HASHREF, optional => 1 },
|
||
dispatch => { type => HASHREF, optional => 1 },
|
||
file => 1,
|
||
encoding => 0,
|
||
class => 0,
|
||
... | ... | |
@{ $_[0]->_errors }
|
||
}
|
||
|
||
sub check_header {
|
||
$_[0]->_check_header;
|
||
}
|
||
|
||
# private stuff
|
||
|
||
sub _open_file {
|
||
... | ... | |
$self->header($header);
|
||
}
|
||
|
||
sub _check_header_for_class {
|
||
my ($self, %params) = @_;
|
||
my @errors;
|
||
|
||
return unless $self->class;
|
||
return $self->header;
|
||
|
||
for my $method (@{ $self->header }) {
|
||
next if $self->class->can($self->_real_method($method));
|
||
|
||
push @errors, [
|
||
$method,
|
||
undef,
|
||
"header field $method is not recognized",
|
||
undef,
|
||
0,
|
||
];
|
||
}
|
||
|
||
$self->_push_error(@errors);
|
||
|
||
return ! @errors;
|
||
}
|
||
|
||
sub _parse_data {
|
||
my ($self, %params) = @_;
|
||
my (@data, @errors);
|
||
... | ... | |
}
|
||
|
||
$self->_data(\@data);
|
||
$self->_errors(\@errors);
|
||
$self->_push_error(@errors);
|
||
|
||
return if @errors;
|
||
return \@data;
|
||
return ! @errors;
|
||
}
|
||
|
||
sub _encode_layer {
|
||
... | ... | |
for my $line (@{ $self->_data }) {
|
||
push @objs, $self->class->new(
|
||
map {
|
||
($self->header_acc && $self->header_acc->{$_}) || $_ => $line->{$_}
|
||
$self->_real_method($_) => $line->{$_}
|
||
} grep { $_ } keys %$line
|
||
);
|
||
}
|
||
... | ... | |
$self->_objects(\@objs);
|
||
}
|
||
|
||
sub _real_method {
|
||
my ($self, $arg) = @_;
|
||
($self->dispatch && $self->dispatch->{$arg}) || $arg;
|
||
}
|
||
|
||
sub _guess_encoding {
|
||
# won't fix
|
||
'utf-8';
|
||
}
|
||
|
||
sub _push_error {
|
||
my ($self, @errors) = @_;
|
||
my @new_errors = ($self->errors, @errors);
|
||
$self->_errors(\@new_errors);
|
||
}
|
||
|
||
|
||
1;
|
||
|
||
... | ... | |
sep_char => ',', # default ';'
|
||
quote_char => ''', # default '"'
|
||
header => [qw(id text sellprice word)] # see later
|
||
header_acc => { sellprice => 'sellprice_as_number' }
|
||
dispatch => { sellprice => 'sellprice_as_number' }
|
||
class => 'SL::DB::CsvLine', # if present, map lines to this
|
||
)
|
||
|
||
... | ... | |
most cases you will want those line to be parsed into hashes or even objects,
|
||
so this model just skips ahead and gives you objects.
|
||
|
||
Encoding autodetection is not easy, and should not be trusted. Try to avoid it if possible.
|
||
Encoding autodetection is not easy, and should not be trusted. Try to avoid it
|
||
if possible.
|
||
|
||
=head1 METHODS
|
||
|
||
... | ... | |
|
||
=item C<encoding>
|
||
|
||
Encoding of the CSV file. Note that this module does not do any encoding guessing.
|
||
Know what your data ist. Defaults to utf-8.
|
||
Encoding of the CSV file. Note that this module does not do any encoding
|
||
guessing. Know what your data ist. Defaults to utf-8.
|
||
|
||
=item C<sep_char>
|
||
|
||
... | ... | |
|
||
=item C<header> \@FIELDS
|
||
|
||
can be an array of columns, in this case the first line is not used as a
|
||
Can be an array of columns, in this case the first line is not used as a
|
||
header. Empty header fields will be ignored in objects.
|
||
|
||
=item C<header_acc> \%ACCESSORS
|
||
=item C<dispatch> \%ACCESSORS
|
||
|
||
May be used to map header fields to custom accessors. Example:
|
||
|
||
... | ... | |
|
||
[
|
||
offending raw input,
|
||
Text::CSV error code if present,
|
||
Text::CSV error diagnostics if present,
|
||
Text::CSV error code if T:C error, 0 else,
|
||
error diagnostics,
|
||
position in line,
|
||
estimated line in file,
|
||
]
|
||
... | ... | |
|
||
Encoding errors are not dealt with properly.
|
||
|
||
=item *
|
||
|
||
Errors are not gathered.
|
||
|
||
=back
|
||
|
||
=head1 TODO
|
||
|
||
Dispatch to child objects, like this:
|
||
|
||
$csv = SL::Helper::Csv->new(
|
||
file => ...
|
||
class => SL::DB::Part,
|
||
dispatch => [
|
||
makemodel => {
|
||
make_1 => make,
|
||
model_1 => model,
|
||
},
|
||
makemodel => {
|
||
make_2 => make,
|
||
model_2 => model,
|
||
},
|
||
]
|
||
);
|
||
|
||
=head1 AUTHOR
|
||
|
||
Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
|
t/helper/csv.t | ||
---|---|---|
$csv = SL::Helper::Csv->new(
|
||
file => \"Kaffee;0.12;12,2;1,5234\n",
|
||
header => [ 'description', 'sellprice', 'lastcost_as_number', 'listprice' ],
|
||
header_acc => { listprice => 'listprice_as_number' },
|
||
dispatch => { listprice => 'listprice_as_number' },
|
||
class => 'SL::DB::Part',
|
||
);
|
||
$csv->parse;
|
||
|
||
is $csv->get_objects->[0]->sellprice, 0.12, 'numeric attr works';
|
||
is $csv->get_objects->[0]->lastcost, 12.2, 'attr helper works';
|
||
is $csv->get_objects->[0]->listprice, 1.5234, 'header_acc works';
|
||
is $csv->get_objects->[0]->listprice, 1.5234, 'dispatch works';
|
||
|
||
#####
|
||
|
||
... | ... | |
EOL
|
||
sep_char => ',',
|
||
quote_char => "'",
|
||
header_acc => { listprice => 'listprice_as_number' },
|
||
dispatch => { listprice => 'listprice_as_number' },
|
||
class => 'SL::DB::Part',
|
||
);
|
||
$csv->parse;
|
Auch abrufbar als: Unified diff
SL/Helper/Csv.pm: header_acc umbenannt in dispatch, Doku, check_header