简体   繁体   中英

Dynamically picking out fields from JSON?

I'm writing something to populate a device data repository by reading JSON output from an agent on servers. I've run into a snag I can't explain.

When working with the nic_bonds I get:

block (2 levels) in translate': undefined method `[]' for nil:NilClass (NoMethodError)

at the line where slaves = is declared. When I change ["#{name}"] to a hardcoded ['bond0'] I end up with what I'm looking for: "bond0"=>"eth2,eth3" .

When I use a similar pattern, working with the Ethernet ports using ["#{name}"] seems to be fine.

What gives?

code:

class DeviceDataTranslator
  def initialize(device_data_collector, device_data_repo)
    @device_data_collector = device_data_collector
    @device_data_repo = device_data_repo
  end

  # translates data between tools
  def translate
    devices_input = @device_data_collector.get_devices_data
    devices_output = []

    # translate data for each node/device
    devices_input.each do |node|

      std_fields = {}
      custom_fields = {}

      # network
      interfaces = node['automatic']['network']['interfaces']
      ethernets = interfaces.select {|k, v| k.match(/^eth/)}
      ethernets.each do |name, data|
        eth_port = {}
        mac = node['automatic']['network'].keys.find {|k| k.include? "macaddress_#{name}"}
        eth_port["#{name}_mac"] = node['automatic']['network']["#{mac}"] unless !mac
        custom_fields.merge!(eth_port)
      end

      nic_bonds = interfaces.select {|k, v| k.match(/^bond/)}
      if nic_bonds
        nic_bonds.each do |name, data|
          bond = {}
          slaves = node['automatic']['ls']['bonding']["#{name}"]['slaves'].join(',')
          bond["#{name}"] = slaves
          custom_fields.merge!(bond)
        end
      end

      # combine fields & add to devices list
      device = [std_fields, custom_fields]
      devices_output.push(device)
    end

    # handoff to device data repo
    @device_data_repo.set_devices(devices_output)
  end
end

And the (reduced) JSON:

    {
      "automatic": {
        "network": {
          "interfaces": {
            "bond0": {
              "type": "bond",
              "number": "0",
              "mtu": "1500"
            },
          "default_interface": "bond0",
          "default_gateway": "x.x.x.x",
          "ipaddress_lo": "127.0.0.1",
          "ipaddress6_lo": "::1",
          "macaddress_eth2": "x-x-x-x-x-x",
          "macaddress_eth3": "x-x-x-x-x-x",
          "macaddress_eth0": "x-x-x-x-x-x",
          "macaddress_eth1": "x-x-x-x-x-x",
          "macaddress_bond0": "x-x-x-x-x-x",
          "ipaddress_bond0": "x.x.x.x"
        },
        "ls": {
          "bonding": {
            "bond0": {
              "slaves": [
                "eth2",
                "eth3"
              ]
            }
          }
        }
      }
    }
  }
block (2 levels) in translate': undefined method `[]' for nil:NilClass (NoMethodError)

Expected when you use a key that doesn't exist.

at the line where slaves = is declared. When I change ["#{name}"] to a hardcoded ['bond0'] I end up with what I'm looking for: "bond0"=>"eth2,eth3".

So clearly name is not bond0 .

When I use a similar pattern working with the ethernet ports using ["#{name}"] seems to be fine.

What gives?

You have a bug somewhere. :) I just ran your sample, with the provided JSON (had to fix a } ), but it works fine. Maybe you exorcised some bad data that is breaking your code? Add statements like this:

p name

In your loops to see what's really in there.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM