如何以编程方式识别哪些密钥在JSON文档中具有子键值对?

时间:2015-01-06 16:33:53

标签: ruby json parsing

我获取了一个JSON文档,需要以编程方式" flatten"另一个第三方服务的关键。

这意味着,如果我的JSON doc回复了以下内容:

{'first_name' => "Joe", 'hoffman' => {'patterns' => ['negativity', 'self-sabotage'], 'right_road' => 'happy family'}, 'mbti' => 'INTJ'}

我需要知道创建一个" flat"第三方服务的键值对,如下所示:

first_name = "Joe"
hoffman.patterns = "negativity, self-sabotage"
hoffman.right_road = "happy family"
mbti = "INTJ"

一旦我知道那里有一个子文档,解析我认为我已经想到只用key + '.' + "{subkey}"附加子键,但是现在,不知道哪些是直键 - 值和哪些人有子文件。

问题:

a)如何解析JSON以了解哪些密钥具有子文档(附加键值)?

b)关于如何从数组创建字符串的建议

3 个答案:

答案 0 :(得分:1)

你也可以将补丁Hash添加到其自己身上,如下所示:

class Hash
  def flatten_keys(prefix=nil)
    each_pair.map do |k,v|
      key = [prefix,k].compact.join(".")
      v.is_a?(Hash) ? v.flatten_keys(key) : [key,v.is_a?(Array) ? v.join(", ") : v]
    end.flatten.each_slice(2).to_a
  end
  def to_flat_hash
    Hash[flatten_keys]
  end
end

那就是

require 'json'
h = JSON.parse(YOUR_JSON_RESPONSE)
#=> {'first_name' => "Joe", 'hoffman' => {'patterns' => ['negativity', 'self-sabotage'], 'right_road' => 'happy family'}, 'mbti' => 'INTJ'}
h.to_flat_hash
#=> {"first_name"=>"Joe", "hoffman.patterns"=>"negativity, self-sabotage", "hoffman.right_road"=>"happy family", "mbti"=>"INTJ"}

也可以使用额外的嵌套

h =  {"first_name"=>"Joe", "hoffman"=>{"patterns"=>["negativity", "self-sabotage"], "right_road"=>"happy family", "wrong_road"=>{"bad_choices"=>["alcohol", "heroin"]}}, "mbti"=>"INTJ"}
h.to_flat_hash
#=> {"first_name"=>"Joe", "hoffman.patterns"=>"negativity, self-sabotage", "hoffman.right_road"=>"happy family", "hoffman.wrong_road.bad_choices"=>"alcohol, heroin", "mbti"=>"INTJ"}

答案 1 :(得分:0)

快速而肮脏的递归过程:

# assuming you've already `JSON.parse` the incoming json into this hash:
a = {'first_name' => "Joe", 'hoffman' => {'patterns' => ['negativity', 'self-sabotage'], 'right_road' => 'happy family'}, 'mbti' => 'INTJ'}

# define a recursive proc:
flatten_keys = -> (h, prefix = "") do
  @flattened_keys ||= {}
  h.each do |key, value|
    # Here we check if there's "sub documents" by asking if the value is a Hash
    # we also pass in the name of the current prefix and key and append a . to it
    if value.is_a? Hash
      flatten_keys.call value, "#{prefix}#{key}."
    else
      # if not we concatenate the key and the prefix and add it to the @flattened_keys hash 
      @flattened_keys["#{prefix}#{key}"] = value
    end
  end
  @flattened_keys
end

flattened = flatten_keys.call a
# => "first_name"=>"Joe", "hoffman.patterns"=>["negativity", "self-sabotage"], "hoffman.right_road"=>"happy family", "mbti"=>"INTJ"}

然后,将数组转换为字符串join

flattened.inject({}) do |hash, (key, value)|
  value = value.join(', ') if value.is_a? Array
  hash.merge! key => value
end

# => {"first_name"=>"Joe", "hoffman.patterns"=>"negativity, self-sabotage", "hoffman.right_road"=>"happy family", "mbti"=>"INTJ"}

答案 2 :(得分:0)

另一种方式,受this post的启发:

def flat_hash(h,f=[],g={})
  return g.update({ f=>h }) unless h.is_a? Hash
  h.each { |k,r| flat_hash(r,f+[k],g) }
  g
end

h = { :a => { :b => { :c => 1,
                      :d => 2 },
              :e => 3 },
      :f => 4 }

result = {}
flat_hash(h) #=> {[:a, :b, :c]=>1, [:a, :b, :d]=>2, [:a, :e]=>3, [:f]=>4}
  .each{ |k, v| result[k.join('.')] = v } #=> {"a.b.c"=>1, "a.b.d"=>2, "a.e"=>3, "f"=>4}