Ruby从json响应中搜索超级嵌套键

时间:2014-03-28 18:46:35

标签: ruby json

我有一个非常嵌套的Json响应。

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

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

我尝试使用此函数extract_list(),但它并没有很好地处理数组。

3 个答案:

答案 0 :(得分: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只是一个例子。如果检查值无法回复到同一个实例,它会失败/产生不正确的结果,并且它可以执行恶意代码:

答案 1 :(得分: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

一般的想法是,给定一个数据结构,你检查它是否为密钥(如果它是一个哈希)并返回匹配值(如果找到)。否则,你迭代它(如果它是一个数组或哈希)并对它的每个孩子执行相同的检查。

这将找到给定键第一次出现的值,如果树中不存在该键则为nil。如果您需要找到所有实例,那么它稍有不同 - 您基本上需要传递一个将累积值的数组:

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

答案 2 :(得分: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}