Skip to content

Commit

Permalink
Add option duplicate_keys (default 1 for now)
Browse files Browse the repository at this point in the history
Default is 1 for now so that users can prepare, and will be set to 0 in the next
release.
  • Loading branch information
perlpunk committed Sep 10, 2020
1 parent 006d03c commit 7a175e1
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 2 deletions.
48 changes: 48 additions & 0 deletions lib/YAML/PP.pm
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ sub new {
my $writer = delete $args{writer};
my $header = delete $args{header};
my $footer = delete $args{footer};
my $duplicate_keys = delete $args{duplicate_keys};
my $yaml_version = $class->_arg_yaml_version(delete $args{yaml_version});
my $default_yaml_version = $yaml_version->[0];
my $version_directive = delete $args{version_directive};
Expand Down Expand Up @@ -67,6 +68,7 @@ sub new {
parser => $parser,
default_yaml_version => $default_yaml_version,
preserve => $preserve,
duplicate_keys => $duplicate_keys,
);
my $dumper = YAML::PP::Dumper->new(
schema => $default_schema,
Expand Down Expand Up @@ -512,6 +514,8 @@ Options:
Values: C<perl> (currently default), C<JSON::PP>, C<boolean>
This option is for loading and dumping.
Note that when dumping, only the chosen boolean style will be recognized.
So if you choose C<JSON::PP>, C<boolean> objects will not be recognized
as booleans and will be dumped as ordinary objects (if you enable the
Expand All @@ -521,6 +525,8 @@ Perl schema).
Default: C<['Core']>
This option is for loading and dumping.
Array reference. Here you can define what schema to use.
Supported standard Schemas are: C<Failsafe>, C<JSON>, C<Core>, C<YAML1_1>.
Expand All @@ -533,17 +539,47 @@ Additionally you can add further schemas, for example C<Merge>.
Default: 'allow' but will be switched to fatal in the future for safety!
This option is for loading only.
Defines what to do when a cyclic reference is detected when loading.
# fatal - die
# warn - Just warn about them and replace with undef
# ignore - replace with undef
# allow - Default
=item duplicate_keys
Default: 1
Since version 0.026
This option is for loading.
NOTE: THIS OPTION WILL BE SET TO 0 IN THE NEXT RELEASE.
The YAML Spec says duplicate mapping keys should be forbidden.
When set to true, duplicate keys in mappings are allowed (and will overwrite
the previous key).
When set to false, duplicate keys will result in an error when loading.
This is especially useful when you have a longer mapping and don't see
the duplicate key in your editor:
---
a: 1
b: 2
# .............
a: 23 # error
=item indent
Default: 2
This option is for dumping.
Use that many spaces for indenting
=item width
Expand All @@ -552,6 +588,8 @@ Since version 0.025
Default: 80
This option is for dumping.
Maximum columns when dumping.
This is only respected when dumping flow collections right now.
Expand All @@ -562,18 +600,24 @@ in the future it will be used also for wrapping long strings.
Default: 1
This option is for dumping.
Print document heaader C<--->
=item footer
Default: 0
This option is for dumping.
Print document footer C<...>
=item yaml_version
Since version 0.020
This option is for loading and dumping.
Default: C<1.2>
Note that in this case, a directive C<%YAML 1.1> will basically be ignored
Expand Down Expand Up @@ -620,6 +664,8 @@ schema.
Since version 0.020
This option is for dumping.
Default: 0
Print Version Directive C<%YAML 1.2> (or C<%YAML 1.1>) on top of each YAML
Expand All @@ -631,6 +677,8 @@ Since version 0.021
Default: false
This option is for loading and dumping.
Preserving scalar styles is still experimental.
use YAML::PP::Common qw/ PRESERVE_ORDER PRESERVE_SCALAR_STYLE /;
Expand Down
12 changes: 12 additions & 0 deletions lib/YAML/PP/Constructor.pm
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ our $VERSION = '0.000'; # VERSION
use YAML::PP;
use YAML::PP::Common qw/ PRESERVE_ALL PRESERVE_ORDER PRESERVE_SCALAR_STYLE PRESERVE_FLOW_STYLE /;
use Scalar::Util qw/ reftype /;
use Carp qw/ croak /;

use constant DEBUG => ($ENV{YAML_PP_LOAD_DEBUG} or $ENV{YAML_PP_LOAD_TRACE}) ? 1 : 0;
use constant TRACE => $ENV{YAML_PP_LOAD_TRACE} ? 1 : 0;
Expand All @@ -18,6 +19,11 @@ sub new {
my ($class, %args) = @_;

my $default_yaml_version = delete $args{default_yaml_version};
# TODO: switch to default 0
my $duplicate_keys = delete $args{duplicate_keys};
unless (defined $duplicate_keys) {
$duplicate_keys = 1;
}
my $preserve = delete $args{preserve} || 0;
if ($preserve == PRESERVE_ALL) {
$preserve = PRESERVE_ORDER | PRESERVE_SCALAR_STYLE | PRESERVE_FLOW_STYLE;
Expand All @@ -36,6 +42,7 @@ sub new {
schemas => $schemas,
cyclic_refs => $cyclic_refs,
preserve => $preserve,
duplicate_keys => $duplicate_keys,
}, $class;
$self->init;
return $self;
Expand Down Expand Up @@ -79,6 +86,7 @@ sub default_yaml_version { return $_[0]->{default_yaml_version} }
sub preserve_order { return $_[0]->{preserve} & PRESERVE_ORDER }
sub preserve_scalar_style { return $_[0]->{preserve} & PRESERVE_SCALAR_STYLE }
sub preserve_flow_style { return $_[0]->{preserve} & PRESERVE_FLOW_STYLE }
sub duplicate_keys { return $_[0]->{duplicate_keys} }

sub document_start_event {
my ($self, $event) = @_;
Expand Down Expand Up @@ -185,12 +193,16 @@ sub mapping_end_event {
}
my $on_data = $last->{on_data} || sub {
my ($self, $hash, $items) = @_;
my %seen;
for (my $i = 0; $i < @$items; $i += 2) {
my ($key, $value) = @$items[ $i, $i + 1 ];
$key = '' unless defined $key;
if (ref $key) {
$key = $self->stringify_complex($key);
}
if ($seen{ $key }++ and not $self->duplicate_keys) {
croak "Duplicate key '$key'";
}
$$hash->{ $key } = $value;
}
};
Expand Down
2 changes: 2 additions & 0 deletions lib/YAML/PP/Loader.pm
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ sub new {
my $cyclic_refs = delete $args{cyclic_refs} || 'allow';
my $default_yaml_version = delete $args{default_yaml_version} || '1.2';
my $preserve = delete $args{preserve};
my $duplicate_keys = delete $args{duplicate_keys};
my $schemas = delete $args{schemas};
$schemas ||= {
'1.2' => YAML::PP->default_schema(
Expand All @@ -27,6 +28,7 @@ sub new {
cyclic_refs => $cyclic_refs,
default_yaml_version => $default_yaml_version,
preserve => $preserve,
duplicate_keys => $duplicate_keys,
);
my $parser = delete $args{parser};
unless ($parser) {
Expand Down
2 changes: 1 addition & 1 deletion t/31.schema.t
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ my $jsonpp = eval { require JSON::PP };

my $schema_file = "$Bin/../ext/yaml-test-schema/yaml-schema.yaml";
my $strings_file = "$Bin/../examples/strings.yaml";
my $schema_data = do { YAML::PP->new->load_file($schema_file) };
my $schema_data = do { YAML::PP->new( duplicate_keys => 1 )->load_file($schema_file) };
my $strings_data = do { YAML::PP->new->load_file($strings_file) };

$schema_data->{'#empty'}->{json_empty_null} = ['null', 'null()', "null"];
Expand Down
36 changes: 36 additions & 0 deletions t/57.dup-keys.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;
use Data::Dumper;
use YAML::PP;
use Test::Deep;

my $allow = YAML::PP->new(
duplicate_keys => 1,
);
my $forbid = YAML::PP->new(
duplicate_keys => 0,
);


my $yaml = <<'EOM';
a: 1
b: 2
a: 3
EOM

my $data = $allow->load_string($yaml);
my $expected = {
a => 3,
b => 2,
};
is_deeply($data, $expected, "Allowed duplicate keys");


$data = eval { $forbid->load_string($yaml) };
my $err = $@;
like $err, qr{Duplicate key 'a'}, "Forbidden duplicate keys";


done_testing;
2 changes: 1 addition & 1 deletion t/lib/YAML/PP/Test.pm
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ sub dump_yaml {
my ($self, $testcase) = @_;
my $id = $testcase->{id};

my $ypp = YAML::PP->new( boolean => 'JSON::PP' );
my $ypp = YAML::PP->new( boolean => 'JSON::PP', duplicate_keys => 1 );
my @docs = eval { $ypp->load_string($testcase->{in_yaml}) };
my $err = $@;
my $result = {};
Expand Down

0 comments on commit 7a175e1

Please sign in to comment.