如何在yaml文件中比较键?

12

有两个 Ruby on Rails 国际化的 YAML 文件。其中一个文件是完整的,另一个文件则有一些缺失的键。我该如何比较这两个 YAML 文件,并查看第二个文件中缺失的键?是否有工具可以做到这一点?


你只想加载YAML文件并遍历其中的值吗?看起来很简单,只是想知道是否有我忽略的东西。 - Devin M
我在想,也许已经有专门完成这项任务的工具了吗? - Jonas
4个回答

16

假设file1是正确的版本,而file2是缺少键值的版本:

def compare_yaml_hash(cf1, cf2, context = [])
  cf1.each do |key, value|

    unless cf2.key?(key)
      puts "Missing key : #{key} in path #{context.join(".")}" 
      next
    end

    value2 = cf2[key]
    if (value.class != value2.class)
      puts "Key value type mismatch : #{key} in path #{context.join(".")}" 
      next
    end

    if value.is_a?(Hash)
      compare_yaml_hash(value, value2, (context + [key]))  
      next
    end
      
    if (value != value2)
      puts "Key value mismatch : #{key} in path #{context.join(".")}" 
    end    
  end
end

现在

compare_yaml_hash(YAML.load_file("file1"), YAML.load_file("file2"))

限制:如果您的YAML文件包含数组,则当前实现应该扩展以支持数组。


2
如果您正在比较两个普通的语言环境文件,这也会失败。在比较它们之前,请删除文件顶部的语言前缀以修复此问题。 - Jonas Schubert Erlandsson
1
@d-Pixie - 比较本地化文件:rails console; I18n.translate(:foo); en = I18n.backend.send(:translations)[:en]; de = I18n.backend.send(:translations)[:de]; compare_yaml_hash(en, de, [])translate(:foo) 是为了强制加载翻译。使用此函数,不同语言将会有许多“键值不匹配”的情况;如果只关心缺少的键,请注释该行。也可以反向运行它。 - Nathan Long
实际上,不要使用 I18n.backend - 它可能有一些键不在你想比较的文件中。 - Nathan Long
@NathanLong 是的,当涉及到这个问题时,I18n有点棘手;)不过总体来说是个好主意。如果你只有一个翻译文件或者想要比较整个区域设置,那么它会起作用。另外,在“键值不匹配”的问题上也很敏锐,当你只需要键时,这有点恼人...还有I18n.backend.send(:init_translations)可以强制I18n初始化,而不是使用I18n.translate(:foo),具体情况可能有所不同。 - Jonas Schubert Erlandsson
@Harish Shetty,上面的代码很好,但我需要相同的代码来比较两个YAML文件,并在Python中填充默认参数,其中键缺失。你能否在这里提供建议?我不知道如何在Python语言的类中编写此函数。 - user3492421

1

differz可以比较两个YAML文件并打印第二个文件中缺失的键。

differz show file1.yml file2.yml

它既可以作为库使用,也可以作为命令行工具使用。您可以使用gem install differz安装后者。

1
我找不到一个快速的工具来完成这个任务。我决定写自己的工具来解决这个问题。
你可以使用cabal安装它:
$ cabal update
$ cabal install yamlkeysdiff

然后您可以以这种方式对两个文件进行差异比较:
$ yamlkeysdiff file1.yml file2.yml
> missing key in file2
< missing key in file1

您可以比较文件的两个子集:
$ yamlkeysdiff "file1.yml#key:subkey" "file2.yml#otherkey"

它的行为与diff完全相同,您可以这样做:
$ yamlkeysdiff file1.yml file2.yml && echo Is the same || echo Is different

解决依赖关系... cabal: 无法解决依赖关系: 尝试: yamlkeysdiff-0.5.1 (用户目标) 下一个目标: base (yamlkeysdiff-0.5.1 的依赖项) 拒绝: base-4.8.2.0/installed-0d6... (冲突: yamlkeysdiff => base>=4.6 && <4.8) 拒绝: base-4.10.0.0, 4.9.1.0, 4.9.0.0, 4.8.2.0, 4.8.1.0, 4.8.0.0, 4.7.0.2, 4.7.0.1, 4.7.0.0, 4.6.0.1, 4.6.0.0, 4.5.1.0, 4.5.0.0, 4.4.1.0, 4.4.0.0, 4.3.1.0, 4.3.0.0, 4.2.0.2, 4.2.0.1, 4.2.0.0, 4.1.0.0, 4.0.0.0, 3.0.3.2, 3.0.3.1 (全局约束需要已安装的实例) 依赖树已彻底搜索。 - InLaw

0
我想提取差异以便进行处理,而这里的代码片段只是打印东西。因此,我的版本返回一个带有差异的哈希表。它的结构反映了原始哈希表,但值是差异的描述。
def deep_hash_diff(hash1, hash2, hash1_name = 'Hash 1', hash2_name = 'Hash 2')
  diff = {}
  (hash1.keys - hash2.keys).each do |key1|
    diff[key1] = "Present in #{hash1_name}, but not in #{hash2_name}"
  end
  (hash2.keys - hash1.keys).each do |key2|
    diff[key2] = "Present in #{hash2_name}, but not in #{hash1_name}"
  end

  (hash1.keys & hash2.keys).each do |common_key|
    if hash1[common_key].is_a?(Hash)
      if hash2[common_key].is_a?(Hash)
        res = deep_hash_diff(hash1[common_key], hash2[common_key], hash1_name, hash2_name)
        diff[common_key] = res if res.present?
      else
        diff[common_key] = "#{hash1_name} has nested hash, but #{hash2_name} just value #{hash2[common_key]}"
      end
    elsif hash2[common_key].is_a?(Hash)
      diff[common_key] = "#{hash2_name} has nested hash, but #{hash1_name} just value #{hash1[common_key]}"
    end
  end
  diff
end

我之后基本上就这样使用它:

res = deep_hash_diff(YAML.load_file("en.yml"), YAML.load_file("spa.yml"), 'English translation', 'Spanish translation')
puts(res.to_yaml) # for nicer output

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接