2014-03-28 31 views
0

我有一个非常嵌套的Json响应。Ruby从json响应中搜索超级嵌套密钥

[[{:test=>[{:id=>1, :b=>{id: '2'}}]}]] 

还有更多的数组,但你明白了。 有没有办法通过递归搜索并找到所有有我需要的密钥的项目?

我试过使用这个函数extract_list(),但它不能很好地处理数组。

+0

JSON响应格式错误。不匹配的括号? ('[{id:1]}')没有值的密钥? ('{test,')括号和大括号的数量不是很多? – Ajedi32

+0

编辑示例使其不那么畸形并且具有相同的密钥两次,因为提到“查找所有具有我需要的密钥的项目”。 –

回答

0

您需要编写自己的递归处理函数。假设你已经转换了您的JSON到一个Ruby数据结构(通过JSON.load或诸如此类的东西):

def deep_find_value_with_key(data, desired_key) 
    case data 
    when Array 
    data.each do |value| 
     if found = deep_find_value_with_key value, desired_key 
     return found 
     end 
    end 
    when Hash 
    if data.key?(desired_key) 
     data[desired_key] 
    else 
     data.each do |key, val| 
     if found = deep_find_value_with_key(val, desired_key) 
      return found 
     end 
     end 
    end 
    end 
    return nil 
end 

总的想法是,给定的数据结构,你选择了它的键(如果它是一个散列),并返回匹配的值,如果找到。否则,你迭代它(如果它是一个Array或Hash)并对它的每个子进行相同的检查。

这将查找给定键的第一个匹配项的值,如果该键不存在于树中,则为零。如果你需要找到所有情况下,那么它的略有不同 - 你基本上需要传递一个阵列,将累积值:

def deep_find_value_with_key(data, desired_key, hits = []) 
    case data 
    when Array 
    data.each do |value| 
     deep_find_value_with_key value, desired_key, hits 
    end 
    when Hash 
    if data.key?(desired_key) 
     hits << data[desired_key] 
    else 
     data.each do |key, val| 
     deep_find_value_with_key(val, desired_key) 
     end 
    end 
    end 

    return hits 
end 
+0

嗯所有的实例(或两者)似乎并没有工作我试着'放deep_find_value_with_key(data,:api).inspect' –

+0

我没有测试代码 - 它可能需要一些调整。但总体思路是正确的 - 您必须对数据进行深度迭代,每次遇到散列时都要检查密钥。你可能需要做一些动作来确保它适合你的情况。 –

0
def nested_find(obj, needed_keys) 
    return {} unless obj.is_a?(Array) || obj.is_a?(Hash) 
    obj.inject({}) do |hash, val| 
    if val.is_a?(Hash) && (tmp = needed_keys & val.keys).length > 0 
     tmp.each { |key| hash[key] = val[key] } 
    elsif val.is_a?(Array) 
     hash.merge!(obj.map { |v| nested_find(v, needed_keys) }.reduce(:merge)) 
    end 
    hash 
    end 
end 

needed_keys = [:id, :another_key] 
nested_find([ ['test', [{id:1}], [[another_key: 5]]]], needed_keys) 
# {:id=>1, :another_key=>5} 
1

下面是不是有什么我建议,但只是为了给出一个简短的替代方案提供的其他解决方案:

2.1.1 :001 > obj = [[{:test=>[{:id=>1, :b=>{id: '2'}}]}]] 
=> [[{:test=>[{:id=>1, :b=>{:id=>"2"}}]}]] 
2.1.1 :002 > key = :id 
=> :id 
2.1.1 :003 > obj.inspect.scan(/#{key.inspect}=>([^,}]*)[,}]/).flatten.map {|s| eval s} 
=> [1, "2"] 

注意:这里使用eval仅仅是一个例子。对于任何其检查值无法评估的事件,它会失败/产生不正确的结果,并且可能执行恶意代码: