2013-08-20 77 views
2

我有一個格式化爲JSON的配置文件,我需要加載到Perl中,然後用從單獨的JSON文件加載的新設置部分覆蓋/擴展。可能會添加/更改的特定設置各不相同,因此我希望儘可能使這種覆蓋靈活。如何在Perl中合併兩個複雜的數據結構?

我的計劃是將一個新的部分配置合併到現有的完整配置對象中 - 無論是JSON還是使用decode_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" 
     } 
    ] 
} 
+2

我與'RIGHT_PRECEDENT'合併和似乎得到你想要的結果哈希::合併的文檔中提到下的ActiveState Perl的Windows上運行的一些問題,或與舊版本的。克隆模塊。Coul這些都是你的問題嗎? – rutter

+1

嗯,我試過RIGHT_PRECEDENT沒有運氣,但它聽起來像只是想要我想要的。我確實在Windows上使用ActiveState distrib,所以這可能是問題...讓我在我的Mac上嘗試它,看看它是否有所改善。 – uptownnickbrown

+1

因此,使用'Hash :: Merge'和'RIGHT_PRECEDENT'確實可以在我的Mac上完美工作......並且在將Clone更新到最新版本之後([Clone-0.34](http://search.cpan.org/~rdf/ Clone-0.23/Clone.pm))它也適用於Windows!感謝提示重新訪問該行爲選項。 – uptownnickbrown

回答

3

因此,原來Hash::Merge只使用O重整在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)