Perl使用Hash::Merge合并blessed哈希时丢失键问题求助
问题原因
你遇到的问题是两个点共同导致的:
Hash::Merge默认不会递归处理被bless过的哈希引用,会直接将其判定为OBJECT类型、当做不可拆分的标量处理,合并时只会保留优先级高的那一个对象的完整内容,不会深入内部合并哈希键值。- 你使用了
LEFT_PRECEDENT(左方优先)策略,调用merge时第一个参数(左参数)是$hash2,所以合并时只会保留$hash2的所有内容,$hash1的内容被完全丢弃,自然就没有helper_1键。
修复方案
你可以选择两种方案解决问题,更推荐第二种更简洁的方案:
方案1:自定义Hash::Merge规则兼容blessed哈希
修改Hash::Merge的行为配置,增加对blessed哈希的合并支持,同时调整参数顺序满足你的合并预期(保留$hash1独有的helper_1,用$hash2的helper_2配置覆盖$hash1的原有配置):
#!usr/bin/perl use strict; use warnings; use Hash::Merge; use Data::Dumper; $Data::Dumper::Sortkeys = 1; my $hash1 = bless( { 'CalcPorts' => { 'helper_1' => { 'Scope' => [ '' ], }, 'helper_2' => { 'Scope' => [ '' ], }, }, }, 'IB' ); my $hash2 = bless( { 'CalcPorts' => { 'helper_2' => { 'Scope' => [ 'd' ], }, }, }, 'IB' ); my $merger = Hash::Merge->new(); # 自定义支持blessed哈希合并的左优先规则 $merger->add_behavior_spec( { 'SCALAR' => { 'SCALAR' => sub { $_[0] }, 'ARRAY' => sub { $_[0] }, 'HASH' => sub { $_[0] }, }, 'ARRAY' => { 'SCALAR' => sub { [ @{$_[0]}, $_[1] ] }, 'ARRAY' => sub { [ @{$_[0]}, @{$_[1]} ] }, 'HASH' => sub { $_[0] }, }, 'HASH' => { 'SCALAR' => sub { $_[0] }, 'ARRAY' => sub { $_[0] }, 'HASH' => sub { Hash::Merge::_merge_hashes( $_[0], $_[1] ) }, }, 'OBJECT' => { 'OBJECT' => sub { # 仅处理同类型的blessed哈希 if (ref $_[0] eq ref $_[1] && ref $_[0] ne 'ARRAY' && ref $_[0] ne 'SCALAR') { return bless(Hash::Merge::_merge_hashes($_[0], $_[1]), ref $_[0]); } return $_[0]; }, 'SCALAR' => sub { $_[0] }, 'ARRAY' => sub { $_[0] }, 'HASH' => sub { $_[0] }, } }, 'LEFT_PRECEDENT_BLESSED' ); # 把$hash2放左位(优先),$hash1放右位 my $hash3 = $merger->merge($hash2, $hash1); print Dumper($hash3);
方案2:先解bless合并再重新bless(更推荐)
不需要修改Hash::Merge的全局规则,先把两个blessed哈希解引用为普通匿名哈希,合并完成后再重新bless回对应类即可:
#!usr/bin/perl use strict; use warnings; use Hash::Merge; use Data::Dumper; $Data::Dumper::Sortkeys = 1; my $hash1 = bless( { 'CalcPorts' => { 'helper_1' => { 'Scope' => [ '' ], }, 'helper_2' => { 'Scope' => [ '' ], }, }, }, 'IB' ); my $hash2 = bless( { 'CalcPorts' => { 'helper_2' => { 'Scope' => [ 'd' ], }, }, }, 'IB' ); my $merger = Hash::Merge->new('LEFT_PRECEDENT'); # 解引用为普通哈希后合并,再重新bless my $hash3 = bless( $merger->merge(+{%$hash2}, +{%$hash1}), 'IB' ); print Dumper($hash3);
运行后输出会符合你的预期,同时保留helper_1和覆盖后的helper_2配置。
内容的提问来源于stack exchange,提问作者urie
相关产品推荐
相关产品推荐

