0

I'm trying to parse out JSON data and create my own dictionary to show a subset of the data. The thing is, I'm noticing that my input data changes based on what is scanned (with nmap). Some elements might be an array value, whereas some might not. The combinations seem to be pretty broad.

For instance, here is the simplest input where only an IP address was found:

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

But then, the IP and MAC address might be found:

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

Those are just a couple examples. Other variations I've seen:

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

As I go through to parse the output, I am first checking if the element is an Array, if it is, I access the key/values one way, whereas if it's not an array, I essentially have to duplicate my code with a few tweaks to it, which doesn't seem very eloquent:

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

In the end, I'm just trying to find a better/eloquent way to produce a hash like below, while accounts for the possibility that an element may/may not be an Array:

{
 '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 => {
   ...
 }
}

If there a ruby method that I haven't run across yet that will make this more fluid?

2 Answers 2

1

Not really... but you can make it always an array eg by doing something like:

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

or similar... then just pass that to your now-non-duplicated code. :)

Sign up to request clarification or add additional context in comments.

2 Comments

Or hosts = Array(hosts) (which may or may not do more than desired).
Because you are using Rails, this looks like the textbook case for Array#wrap. The suggested answer will give some unexpected results; for example, if hosts == nil you will get [nil].
0

The 20 lines of code in your question can be reduced to a single line using Array#wrap instead of conditionals, and using Enumerable#map instead of Enumerable#each:

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

Now that's magic!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.