Ruby&获取哈希值魔术

时间:2018-03-04 22:03:39

标签: ruby-on-rails json ruby

我正在尝试解析JSON数据并创建自己的字典来显示数据的子集。问题是,我注意到我的输入数据根据扫描的内容而改变(使用nmap)。一些元素可能是数组值,而有些可能不是。这些组合看起来相当广泛。

例如,这是最简单的输入,其中只找到了IP地址:

{
  'host' => {
    'address' => {
      'addr' => '192.168.0.1'
    },
    'status' => {...}
  }
}

但是,可能会找到IP和MAC地址:

{
  'host' => {
    'address' => [{
      'addrtype' => 'ipv4',
      'addr' => '192.168.0.1',
     },{
      'addrtype' => 'mac',
      'mac' => '00:AA:BB:CC:DD:EE',
     },
    'status' => {...}
  }]
}

这只是几个例子。我见过的其他变化:

`host.class` = Array
`address.class` = Hash
`host['status'].class` = Array
etc...

当我解析输出时,我首先检查元素是否是一个数组,如果是,我以一种方式访问​​键/值,而如果它不是一个数组,我基本上必须复制我的代码对它进行一些调整,这似乎不是很有说服力:

hash = {}
  if hosts.class == Array
    hosts.each do |host|
      ip = if host['address'].class == Array
             host['address'][0]['addr']
           else
             host['address']['addr']
           end
      hash[ip] = {}
    end
  else
    ip = if hosts['address'].class == Array
           hosts['address'][0]['addr']
         else
           hosts['address']['addr']
         end
    hash[ip] = {}
  end
  puts hash
end

最后,我只是想找到一种更好/更有说服力的方法来生成如下所示的哈希,同时考虑一个元素可能/可能不是数组的可能性:

{
 '192.168.0.1' => {
   'mac' => '00:aa:bb:cc:dd:ee',
   'vendor' => 'Apple',
   'ports' => {
      '80' => {
        'status' => 'open',
        'service' => 'httpd'
      }
      '443' => {
        'status' => 'filtered',
        'service' => 'httpd'
      }
    }
 },
 192.168.0.2 => {
   ...
 }
}

如果有一种我尚未遇到过的红宝石方法会让它更流畅吗?

2 个答案:

答案 0 :(得分:1)

不是真的......但是你可以把它变成一个数组,例如通过做类似的事情:

hosts = [hosts] unless hosts.is_a?(Array)

或类似...然后将其传递给您现在不重复的代码。 :)

答案 1 :(得分:0)

使用Array#wrap代替条件,并使用Enumerable#map代替Enumerable#each,可以将问题中的20行代码缩减为一行:

Array.wrap(hosts).map { |host| [Array.wrap(host['address']).first['addr'], {}] }.to_h

现在真是太神奇了!