【问题标题】:Ruby search for super nested key from json responseRuby从json响应中搜索超级嵌套键
【发布时间】:2014-03-29 04:21:29
【问题描述】:

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

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

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

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

【问题讨论】:

  • 该 JSON 响应格式错误。括号不匹配? ([{id:1]}) 没有价值的钥匙? ({test,) 括号和大括号的数量不一样?
  • 编辑示例以减少格式错误并使用相同的密钥两次,因为提到“找到所有具有我需要的密钥的项目”。

标签: ruby json


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

【讨论】:

    【解决方案2】:

    您需要编写自己的递归处理程序。假设您已经将 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。如果您需要查找 all 实例,那么情况略有不同 - 您基本上需要传递一个将累积值的数组:

    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
    

    【讨论】:

    • 嗯,所有实例(或任何一个)似乎都不起作用我试过puts deep_find_value_with_key(data, :api).inspect
    • 我没有测试代码 - 它可能需要一些调整。但总体思路是正确的——你必须对数据进行深度迭代,每次遇到哈希时都要检查键。您可能需要做一些跑腿工作,以确保它适用于您的情况。
    【解决方案3】:
    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}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-04-20
      • 1970-01-01
      • 2020-09-09
      • 2018-05-28
      • 1970-01-01
      • 2019-02-17
      • 1970-01-01
      • 2018-06-23
      相关资源
      最近更新 更多