我正在寻找一种方法来比较(以及最好的情况,也是差异)Ruby中的两个YAML文件;无论关键顺序如何,自然而然。到目前为止,我发现的所有解决方案都依赖于使用YAML::load_file()
加载文件。但是,我不能这样做,因为文件是我没有的类声明的Ruby对象的转储,因此加载它们会抛出undefined class/module
。
我认为我需要将它们作为字符串哈希加载并进行比较,但是如何告诉Ruby忽略类型信息并将其包含在比较中呢?
基于评论:我基本上对基于文本的比较感兴趣,但它必须知道"深度"的数据结构。例如,这是我所拥有的一个文件的摘录:
attributes: !ruby/hash:Example::Attributes
!binary "b2NjaQ==": !ruby/hash:Example::Attributes
!binary "Y29yZQ==": !ruby/hash:Example::Attributes
!binary "aWQ=": !ruby/object:Example::Properties
type: string
required: false
mutable: false
!binary "dGl0bGU=": !ruby/object:Example::Properties
type: string
required: false
mutable: false
因此,即使两个属性的顺序相反,比较也必须能够识别匹配。
答案 0 :(得分:2)
Psych,Ruby的Yaml解析器,提供了几种检查Yaml数据的方法。 highest level加载Yaml并提供Ruby数据结构。这是查看Yaml标记并尝试加载适当的Ruby类的API,这会导致您的问题。它还会查看数据的格式,并在匹配时将其转换为各种类型(例如日期)。
next level将解析Yaml并为您提供包含“原始”Yaml数据的AST。高级API通过首先解析到此AST然后使用visitor pattern遍历它来创建Ruby数据(通常是哈希或数组)来工作。不幸的是,它没有提供这两个级别之间的任何内容,但是创建一个创建简化数据结构的解析器相当容易。
其核心Yaml数据基本上由标量(基本上是字符串),映射(散列)和序列(数组)组成 - 所有这些都可以有与之关联的标记。 Psych提供的AST由这三种类型(以及其他几种类型)组成,我们可以创建自己的访问者遍历它并生成一个仅由哈希,数组和字符串组成的Ruby结构。
这是基于Psych ToRuby
visitor class的松散基础,但它不是试图将数据转换为适当的Ruby类型,而是仅创建数组,哈希和字符串,丢弃标记中的任何数据:
require 'psych'
class ToPlain < Psych::Visitors::Visitor
# Scalars are just strings.
def visit_Psych_Nodes_Scalar o
o.value
end
# Sequences are arrays.
def visit_Psych_Nodes_Sequence o
o.children.each_with_object([]) do |child, list|
list << accept(child)
end
end
# Mappings are hashes.
def visit_Psych_Nodes_Mapping o
o.children.each_slice(2).each_with_object({}) do |(k,v), h|
h[accept(k)] = accept(v)
end
end
# We also need to handle documents...
def visit_Psych_Nodes_Document o
accept o.root
end
# ... and streams.
def visit_Psych_Nodes_Stream o
o.children.map { |c| accept c }
end
# Aliases aren't handles here :-(
def visit_Psych_Nodes_Alias o
# Not implemented!
end
end
(注意这不会处理别名。添加对它们的支持并不太难,看看ToRuby
做了什么,特别是register
method以及它是如何使用的。)
你可以这样使用:
# Could also use parse_stream or parse_file here
ast = YAML.parse(my_data)
data = ToPlain.new.accept(ast)
# data now consists of just arrays, hashes and strings
如果在示例数据上使用此结果,则结果是一个如下所示的哈希:
{
"attributes"=>{
"b2NjaQ=="=>{
"Y29yZQ=="=>{
"aWQ="=>{
"type"=>"string",
"required"=>"false",
"mutable"=>"false"
},
"dGl0bGU="=>{
"type"=>"string",
"required"=>"false",
"mutable"=>"false"
}
}
}
}
}
虽然由于您使用的是二进制数据,因此键很不实用,但您仍然可以进行这样的比较:
occi_core_id = data["attributes"]["b2NjaQ=="]["Y29yZQ=="]["aWQ="]
occi_core_title = data["attributes"]["b2NjaQ=="]["Y29yZQ=="]["dGl0bGU="]
puts occi_core_id == occi_core_title