我有一个格式化为JSON的配置文件,我需要将其加载到Perl中,然后使用从单独的JSON文件加载的新设置部分覆盖/扩展。可能添加/更改的特定设置各不相同,因此我希望尽可能灵活地使用此覆盖。
我的计划是在使用decode_json之后将新的部分配置合并到现有的完整配置对象中 - 使用JSON或Perl嵌套数据结构。转换效果很好。
有没有一种简单有效的方法在Perl中执行此操作,除了递归遍历我的复杂数据结构并进行大量特定比较之外?我查看了Hash :: Merge,它正在破坏我的数据。问题似乎是它查看高级键/值对(下面的“config”或“bookToolbar”)并覆盖该高级别的完整键/值对。我想要的是搜索深度优先并覆盖它可以使用的最具体的值,同时保持原始的其他键/值对。
例如,这是一个“完整”配置:
{
"config" : {
"bookToolbar" : {
"highlights" : {
"enabled" : false
},
"bookmark" : {
"enabled" : false
}
},
"pageAspectRatio" : {
"width" : "432",
"height" : "648"
},
"highlighter" : {
"sharedColor" : "#000000",
"colors" : [
"#ffff00"
]
}
"mainMenu" : {
"index" : {
"dataPath" : "data/index/",
"enabled" : false
},
"media" : {
"dataPath" : "data/media.xml",
"enabled" : false
},
"toc" : {
"dataPath" : "data/toc.xml"
},
"glossary" : {
"audioPath" : "audio/glossary/",
"dataPath" : "data/glossary.xml",
"imagePath" : "img/glossary/",
"enabled" : false
}
}
},
"pagelist" : [{
"hasOnPageNotes" : true,
"pageName" : "cover",
"hasScreenReader" : false,
"hasTextMarkup" : true,
"hasLinks" : false,
"pageId" : "cover"
}, {
"hasOnPageNotes" : true,
"pageName" : "1",
"hasScreenReader" : false,
"hasTextMarkup" : true,
"hasLinks" : false,
"pageId" : "1"
}
]
}
以下是我想用来部分覆盖/扩展上述内容的数据:
{
"config" : {
"bookToolbar" : {
"bookmark" : {
"enabled" : true
},
"help" : {
"data" : {
"url" : "aGreatHelpFile.html"
},
"enabled" : true
},
"links" : {
"enabled" : true
}
}
},
"pagelist" : [{
"hasOnPageNotes" : true,
"pageName" : "2",
"hasScreenReader" : false,
"hasTextMarkup" : true,
"hasLinks" : false,
"pageId" : "2"
}
]
}
我想要的输出是:
{
"config" : {
"bookToolbar" : {
"highlights" : {
"enabled" : false
},
"help" : {
"data" : {
"url" : "aGreatHelpFile.html"
},
"enabled" : true
},
"bookmark" : {
"enabled" : true
}
"links" : {
"enabled" : false
}
},
"pageAspectRatio" : {
"width" : "432",
"height" : "648"
},
"highlighter" : {
"sharedColor" : "#000000",
"colors" : [
"#ffff00"
]
},
"mainMenu" : {
"index" : {
"dataPath" : "data/index/",
"enabled" : false
},
"media" : {
"dataPath" : "data/media.xml",
"enabled" : false
},
"toc" : {
"dataPath" : "data/toc.xml"
},
"glossary" : {
"audioPath" : "audio/glossary/",
"dataPath" : "data/glossary.xml",
"imagePath" : "img/glossary/",
"enabled" : false
}
}
},
"pagelist" : [{
"hasOnPageNotes" : true,
"pageName" : "cover",
"hasScreenReader" : false,
"hasTextMarkup" : true,
"hasLinks" : false,
"pageId" : "cover"
}, {
"hasOnPageNotes" : true,
"pageName" : "1",
"hasScreenReader" : false,
"hasTextMarkup" : true,
"hasLinks" : false,
"pageId" : "1"
}, {
"hasOnPageNotes" : true,
"pageName" : "2",
"hasScreenReader" : false,
"hasTextMarkup" : true,
"hasLinks" : false,
"pageId" : "2"
}
]
}
答案 0 :(得分:4)
事实证明,Hash::Merge
仅使用过时版本的Clone模块在Windows上破坏我的数据。
使用最新版本(或立即在我的Mac上),以下代码完全符合我的需要:
#!/usr/bin/env perl -w
use strict;
use JSON;
use Hash::Merge qw( merge );
Hash::Merge::set_behavior('RIGHT_PRECEDENT');
# Load full config into hashref
open (IN, "<:utf8", "full-config.txt");
my $app_data;
while(<IN>) {$app_data .= $_;}
my $app_json = decode_json($app_data);
close IN;
# Sample portion of config options to override/extend
my $app_override = '{"config": {
"bookToolbar": {
"bookmark": {
"enabled":false
}, "help": {
"data": {
"url":"aGreatHelpFile.html"
}, "enabled":true
}, "closeBook": {
"enabled":true
}
}
},
"pagelist":[
{
"hasOnPageNotes" : true,
"pageName" : "25",
"hasTextMarkup" : true,
"hasScreenReader" : false,
"hasLinks" : false,
"pageId" : "0025"
}
]
}';
my $app_override_hash = from_json($app_override, {utf8 => 1});
# Merge with right precedent, $app_json hash ref has everything we need.
$app_json = merge( $app_json, $app_override_hash );
我发现此表非常有助于分析Hash::Merge
中的不同优先级选项(这是straight from the docs:
LEFT TYPE RIGHT TYPE LEFT_PRECEDENT RIGHT_PRECEDENT
SCALAR SCALAR $a $b
SCALAR ARRAY $a ( $a, @$b )
SCALAR HASH $a %$b
ARRAY SCALAR ( @$a, $b ) $b
ARRAY ARRAY ( @$a, @$b ) ( @$a, @$b )
ARRAY HASH ( @$a, values %$b ) %$b
HASH SCALAR %$a $b
HASH ARRAY %$a ( values %$a, @$b )
HASH HASH merge( %$a, %$b ) merge( %$a, %$b )
LEFT TYPE RIGHT TYPE STORAGE_PRECEDENT RETAINMENT_PRECEDENT
SCALAR SCALAR $a ( $a ,$b )
SCALAR ARRAY ( $a, @$b ) ( $a, @$b )
SCALAR HASH %$b merge( hashify( $a ), %$b )
ARRAY SCALAR ( @$a, $b ) ( @$a, $b )
ARRAY ARRAY ( @$a, @$b ) ( @$a, @$b )
ARRAY HASH %$b merge( hashify( @$a ), %$b )
HASH SCALAR %$a merge( %$a, hashify( $b ) )
HASH ARRAY %$a merge( %$a, hashify( @$b ) )
HASH HASH merge( %$a, %$b ) merge( %$a, %$b )