Revision f74b0dac
Von Sven Schöling vor mehr als 8 Jahren hinzugefügt
SL/Helper/Csv.pm | ||
---|---|---|
204 | 204 |
foreach my $h (@{ $h_aref }) { |
205 | 205 |
my @names = ( |
206 | 206 |
keys %{ $self->profile->[$p_num]->{profile} || {} }, |
207 |
keys %{ $self->profile->[$p_num]->{mapping} || {} }, |
|
207 | 208 |
); |
208 | 209 |
for my $name (@names) { |
209 | 210 |
for my $i (0..$#$h) { |
... | ... | |
527 | 528 |
|
528 | 529 |
PROFILE := [ CLASS_PROFILE, CLASS_PROFILE* ] |
529 | 530 |
CLASS_PROFILE := { |
530 |
profile => { ACCESSORS }, |
|
531 |
profile => { ACCESSORS+ },
|
|
531 | 532 |
class => $classname, |
532 | 533 |
row_ident => $row_ident, |
534 |
mapping => { MAPPINGS* }, |
|
533 | 535 |
} |
534 |
ACCESSORS := $field => $accessor, ACCESSORS* |
|
536 |
ACCESSORS := $field => $accessor |
|
537 |
MAPPINGS := $alias => $field |
|
535 | 538 |
|
536 |
The C<profile> is a HASHREF which may be used to map header fields to custom
|
|
539 |
The C<ACCESSORS> may be used to map header fields to custom
|
|
537 | 540 |
accessors. Example: |
538 | 541 |
|
539 |
[ |
|
540 |
{ |
|
541 |
profile => { |
|
542 |
listprice => 'listprice_as_number', |
|
543 |
} |
|
544 |
} |
|
545 |
] |
|
542 |
profile => { |
|
543 |
listprice => 'listprice_as_number', |
|
544 |
} |
|
546 | 545 |
|
547 | 546 |
In this case C<listprice_as_number> will be used to store the values from the |
548 | 547 |
C<listprice> column. |
... | ... | |
575 | 574 |
C<row_ident> is used to determine the correct profile in multiplexed data and |
576 | 575 |
must be given there. It's not used in non-multiplexed data. |
577 | 576 |
|
578 |
Example: |
|
577 |
If C<mappings> is present, it must contain a hashref that maps strings to known |
|
578 |
fields. This can be used to add custom profiles for known sources, that don't |
|
579 |
comply with the expected header identities. |
|
580 |
|
|
581 |
Without strict profiles, mappings can also directly map header fields that |
|
582 |
should end up in the same accessor. |
|
583 |
|
|
584 |
Mappings can be identical to known fields and will be prefered during lookup, |
|
585 |
but will not replace the field, meaning that: |
|
586 |
|
|
587 |
profile => { |
|
588 |
name => 'name', |
|
589 |
description => 'description', |
|
590 |
} |
|
591 |
mapping => { |
|
592 |
name => 'description', |
|
593 |
shortname => 'name', |
|
594 |
} |
|
595 |
|
|
596 |
will work as expected, and shortname will not end up in description. This also |
|
597 |
works with the case insensitive option. Note however that the case insensitive |
|
598 |
option will not enable true unicode collating. |
|
599 |
|
|
600 |
|
|
601 |
Here's a full example: |
|
602 |
|
|
579 | 603 |
[ |
580 | 604 |
{ |
581 | 605 |
class => 'SL::DB::Order', |
... | ... | |
584 | 608 |
{ |
585 | 609 |
class => 'SL::DB::OrderItem', |
586 | 610 |
row_ident => 'I', |
587 |
profile => { sellprice => 'sellprice_as_number' } |
|
611 |
profile => { sellprice => 'sellprice_as_number' }, |
|
612 |
mapping => { 'Verkaufspreis' => 'sellprice' } |
|
588 | 613 |
}, |
589 | 614 |
] |
590 | 615 |
|
... | ... | |
601 | 626 |
|
602 | 627 |
Note that the last entry can be off, but will give an estimate. |
603 | 628 |
|
629 |
Error handling is also known to break on new Perl versions and need to be |
|
630 |
adjusted from time to time due to changes in Text::CSV_XS. |
|
631 |
|
|
604 | 632 |
=head1 CAVEATS |
605 | 633 |
|
606 | 634 |
=over 4 |
SL/Helper/Csv/Dispatcher.pm | ||
---|---|---|
113 | 113 |
my $i = 0; |
114 | 114 |
foreach my $header (@{ $h_aref }) { |
115 | 115 |
my $spec = $self->_parse_profile(profile => $csv_profile->[$i]->{profile}, |
116 |
mapping => $csv_profile->[$i]->{mapping}, |
|
116 | 117 |
class => $csv_profile->[$i]->{class}, |
117 | 118 |
header => $header); |
118 | 119 |
push @specs, $spec; |
... | ... | |
132 | 133 |
my $profile = $params{profile}; |
133 | 134 |
my $class = $params{class}; |
134 | 135 |
my $header = $params{header}; |
136 |
my $mapping = $params{mapping}; |
|
135 | 137 |
|
136 | 138 |
my @specs; |
137 | 139 |
|
138 | 140 |
for my $col (@$header) { |
139 | 141 |
next unless $col; |
140 |
if ($self->_csv->strict_profile) {
|
|
141 |
if (exists $profile->{$col}) {
|
|
142 |
push @specs, $self->make_spec($col, $profile->{$col}, $class);
|
|
143 |
} else {
|
|
144 |
$self->unknown_column($col, undef);
|
|
145 |
}
|
|
142 |
if (exists $mapping->{$col} && $profile->{$mapping->{$col}}) {
|
|
143 |
push @specs, $self->make_spec($col, $profile->{$mapping->{$col}}, $class);
|
|
144 |
} elsif (exists $mapping->{$col}) {
|
|
145 |
push @specs, $self->make_spec($col, $mapping->{$col}, $class);
|
|
146 |
} elsif (exists $profile->{$col}) {
|
|
147 |
push @specs, $self->make_spec($col, $profile->{$col}, $class);
|
|
146 | 148 |
} else { |
147 |
if (exists $profile->{$col}) {
|
|
148 |
push @specs, $self->make_spec($col, $profile->{$col}, $class);
|
|
149 |
if ($self->_csv->strict_profile) {
|
|
150 |
$self->unknown_column($col, undef);
|
|
149 | 151 |
} else { |
150 | 152 |
push @specs, $self->make_spec($col, $col, $class); |
151 | 153 |
} |
t/helper/csv.t | ||
---|---|---|
1 |
use Test::More tests => 75;
|
|
1 |
use Test::More tests => 84;
|
|
2 | 2 |
|
3 | 3 |
use lib 't'; |
4 | 4 |
use utf8; |
... | ... | |
726 | 726 |
|
727 | 727 |
##### |
728 | 728 |
|
729 |
# Mappings |
|
730 |
# simple case |
|
731 |
$csv = SL::Helper::Csv->new( |
|
732 |
file => \<<EOL, |
|
733 |
description,sellprice,lastcost_as_number,purchaseprice, |
|
734 |
Kaffee,0.12,'12,2','1,5234' |
|
735 |
EOL |
|
736 |
sep_char => ',', |
|
737 |
quote_char => "'", |
|
738 |
profile => [ |
|
739 |
{ |
|
740 |
profile => { listprice => 'listprice_as_number' }, |
|
741 |
mapping => { purchaseprice => 'listprice' }, |
|
742 |
class => 'SL::DB::Part', |
|
743 |
} |
|
744 |
], |
|
745 |
); |
|
746 |
ok $csv->parse, 'simple mapping parses'; |
|
747 |
is $csv->get_objects->[0]->listprice, 1.5234, 'simple mapping works'; |
|
748 |
|
|
749 |
$csv = SL::Helper::Csv->new( |
|
750 |
file => \<<EOL, |
|
751 |
description;partnumber;sellprice;purchaseprice;wiener; |
|
752 |
Kaffee;;0.12;1,221.52;ja wiener |
|
753 |
Beer;1123245;0.12;1.5234;nein kein wieder |
|
754 |
EOL |
|
755 |
numberformat => '1,000.00', |
|
756 |
ignore_unknown_columns => 1, |
|
757 |
strict_profile => 1, |
|
758 |
profile => [{ |
|
759 |
profile => { lastcost => 'lastcost_as_number' }, |
|
760 |
mapping => { purchaseprice => 'lastcost' }, |
|
761 |
class => 'SL::DB::Part', |
|
762 |
}] |
|
763 |
); |
|
764 |
ok $csv->parse, 'strict mapping parses'; |
|
765 |
is $csv->get_objects->[0]->lastcost, 1221.52, 'strict mapping works'; |
|
766 |
|
|
767 |
# swapping |
|
768 |
$csv = SL::Helper::Csv->new( |
|
769 |
file => \<<EOL, |
|
770 |
description;partnumber;sellprice;lastcost;wiener; |
|
771 |
Kaffee;1;0.12;1,221.52;ja wiener |
|
772 |
Beer;1123245;0.12;1.5234;nein kein wieder |
|
773 |
EOL |
|
774 |
numberformat => '1,000.00', |
|
775 |
ignore_unknown_columns => 1, |
|
776 |
strict_profile => 1, |
|
777 |
profile => [{ |
|
778 |
mapping => { partnumber => 'description', description => 'partnumber' }, |
|
779 |
class => 'SL::DB::Part', |
|
780 |
}] |
|
781 |
); |
|
782 |
ok $csv->parse, 'swapping parses'; |
|
783 |
is $csv->get_objects->[0]->partnumber, 'Kaffee', 'strict mapping works 1'; |
|
784 |
is $csv->get_objects->[0]->description, '1', 'strict mapping works 2'; |
|
785 |
|
|
786 |
# case insensitive shit |
|
787 |
$csv = SL::Helper::Csv->new( |
|
788 |
file => \"Description\nKaffee", # " # make emacs happy |
|
789 |
case_insensitive_header => 1, |
|
790 |
profile => [{ |
|
791 |
mapping => { description => 'description' }, |
|
792 |
class => 'SL::DB::Part' |
|
793 |
}], |
|
794 |
); |
|
795 |
$csv->parse; |
|
796 |
is $csv->get_objects->[0]->description, 'Kaffee', 'case insensitive mapping without profile works'; |
|
797 |
|
|
798 |
# case insensitive shit |
|
799 |
$csv = SL::Helper::Csv->new( |
|
800 |
file => \"Price\n4,99", # " # make emacs happy |
|
801 |
case_insensitive_header => 1, |
|
802 |
profile => [{ |
|
803 |
profile => { sellprice => 'sellprice_as_number' }, |
|
804 |
mapping => { price => 'sellprice' }, |
|
805 |
class => 'SL::DB::Part', |
|
806 |
}], |
|
807 |
); |
|
808 |
$csv->parse; |
|
809 |
is $csv->get_objects->[0]->sellprice, 4.99, 'case insensitive mapping with profile works'; |
|
810 |
|
|
729 | 811 |
|
730 | 812 |
# vim: ft=perl |
731 | 813 |
# set emacs to perl mode |
Auch abrufbar als: Unified diff
Csv: mapping support