因此,项目数组不是项目数组,而是子模式的属性数组。这是考虑到这一事实的新解决方案:
schema =
{ "type"=>"object",
"properties"=>{
"books"=>{
"type"=>"array",
"items"=> {
"type"=>"object",
"properties" => {
"urn" => { "type"=>"string" },
"title" => { "type"=>"string" }
}
} # end items
} # end books
} # end properties
} # end schema
tree = {"name"=>"200"}
def build_tree(schema, tree, level)
puts
puts "level=#{level} schema[:type]=#{schema['type'].inspect}, schema class is #{schema.class}"
puts "level=#{level} tree=#{tree}"
case schema['type']
when 'object'
puts "in when object for #{schema['properties'].size} properties :"
i = 0
schema['properties'].each_key{ | name | puts "#{i+=1}. #{name}" }
tree[:children] = []
schema['properties'].each do | property_name, property_schema |
puts "object level=#{level}, property_name=#{property_name}"
type, sub_tree = build_tree(property_schema, {}, level + 1)
puts "object level=#{level} after recursion, type=#{type} sub_tree=#{sub_tree}"
child = { name: property_name + type }
sub_tree.each { | k, v | child[k] = v }
tree[:children] << child
end
puts "object level=#{level} about to return tree=#{tree}"
tree
when 'array'
puts "in when array"
case schema['items']
when Hash
puts "in when Hash"
puts "the schema has #{schema['items'].keys.size} keys :"
schema['items'].keys.each{ | key | puts key }
# here you could raise an error if the two keys are NOT "type"=>"object" and "properties"=>{ ... }
puts "Hash level=#{level} about to recurs"
return ' (array)', build_tree(schema['items'], {}, level + 1)
else
puts "oops ! Hash expected"
"oops ! Hash expected"
end
when 'string'
puts "in when string, schema=#{schema}"
return ' (string)', {}
else
puts "in else"
tree[:name] == schema # ???? comparison ?
end
end
build_tree(schema, tree, 1)
puts 'final result :'
puts tree
编辑结果(使用 ruby 2.3.3p222 测试):
{ "name"=>"200",
:children=> [
{
:name=>"books (array)",
:children=> [
{:name=>"urn (string)"},
{:name=>"title (string)"}
]
}
]
}
不要将其视为出色的代码。每次 12 级地震我都会编写 Ruby 代码。目的是解释代码中哪些内容不起作用,并引起人们注意在递归调用中使用新变量(现在是空哈希)。有很多情况需要测试并引发错误。
正确的方法是 BDD,就像 @moveson 一样:首先为所有情况(尤其是边缘情况)编写 RSpec 测试,然后编写代码。我知道它给人的感觉是太慢了,但从长远来看,它是值得的,并且取代了跟踪的调试和打印。
有关测试的更多信息
此代码很脆弱:例如,如果类型键未与属性键关联,则它将失败schema['properties'].each
: undefined method 'each' for nil:NilClass
。像这样的规格context 'when a type object has no properties' do
let(:schema) { {"type" => "object", "xyz" => ...
将有助于添加代码来检查先决条件。我也懒得在小脚本中使用 RSpec,但对于严肃的开发,我会付出努力,因为我已经认识到了它的好处。花在调试上的时间就永远消失了,投入在规范上的时间可以在发生变化时提供安全性,并提供关于代码做什么或不做什么的清晰易读的缩进报告。我推荐全新的Rspec 3 本书 https://pragprog.com/book/rspec3/effective-testing-with-rspec-3.
关于访问哈希的更多信息:如果混合使用字符串和符号,则会出现问题。
some_key = some_data # sometimes string, sometimes symbol
schema[some_key]...
如果内部键与外部数据的类型不同,则将找不到该元素。创建哈希时选择一种类型,例如符号,并系统地将访问变量转换为符号:
some_key = some_data # sometimes string, sometimes symbol
schema[some_key.to_sym]...
或全部转为字符串:
some_key = some_data # sometimes string, sometimes symbol
schema[some_key.to_s]...