How can I get ruby's JSON to follow object references like Pry/PP?

301 views Asked by At

I've stared at this so long I'm going in circles...

I'm using the rbvmomi gem, and in Pry, when I display an object, it recurses down thru the structure showing me the nested objects - but to_json seems to "dig down" into some objects, but just dump the reference for others> Here's an example:

[24] pry(main)> g
=> [GuestNicInfo(
   connected: true,
   deviceConfigId: 4000,
   dynamicProperty: [],
   ipAddress: ["10.102.155.146"],
   ipConfig: NetIpConfigInfo(
     dynamicProperty: [],
     ipAddress: [NetIpConfigInfoIpAddress(
        dynamicProperty: [],
        ipAddress: "10.102.155.146",
        prefixLength: 20,
        state: "preferred"
      )]
   ),
   macAddress: "00:50:56:a0:56:9d",
   network: "F5_Real_VM_IPs"
 )]
[25] pry(main)> g.to_json
=> "[\"#<RbVmomi::VIM::GuestNicInfo:0x000000085ecc68>\"]"

Pry apparently just uses a souped-up pp, and while "pp g" gives me close to what I want, I'm kinda steering as hard as I can toward json so that I don't need a custom parser to load up and manipulate the results.

The question is - how can I get the json module to dig down like pp does? And if the answer is "you can't" - any other suggestions for achieving the goal? I'm not married to json - if I can get the data serialized and read it back later (without writing something to parse pp output... which may already exist and I should look for it), then it's all win.

My "real" goal here is to slurp up a bunch of info from our vsphere stuff via rbvmomi so that I can do some network/vm analysis on it, which is why I'd like to get it in a nice machine-parsed format. If I'm doing something stupid here and there's an easier way to go about this - lay it on me, I'm not proud. Thank you all for your time and attention.

Update: Based on Arnie's response, I added this monkeypatch to my script:

class RbVmomi::BasicTypes::DataObject
   def to_json(*args)
      h = self.props
      m = h.merge({ JSON.create_id => self.class.name })
      m.to_json(*args)
   end
end

and now my to_json recurses down nicely. I'll see about submitting this (or the def, really) to the project.

1

There are 1 answers

2
Arie Xiao On BEST ANSWER

The .to_json works in a recursive manner, the default behavior is defined as:

Converts this object to a string (calling to_s), converts it to a JSON string, and returns the result. This is a fallback, if no special method to_json was defined for some object.

json library has added some implementation for some common classes (check the left hand side of this documentation), such as Array, Range, DateTime.

For an array, to_json first convert all the elements to json object, concat then together, and then add the array mark [/].

For your case, you need to define your customized to_json method for GuestNicInfo, NetIpConfigInfo and NetIpConfigInfoIpAddress. I don't know your implementation about these three classes, so I wrote a example to demonstrate how to achieve this:

require 'json'

class MyClass
  attr_accessor :a, :b
  def initialize(a, b)
    @a = a
    @b = b
  end
end

data = [MyClass.new(1, "foobar")]
puts data.to_json
#=> ["#<MyClass:0x007fb6626c7260>"]    

class MyClass
  def to_json(*args)
    {
      JSON.create_id => self.class.name,
      :a => a,
      :b => b
    }.to_json(*args)
  end
end

puts data.to_json
#=> [{"json_class":"MyClass","a":1,"b":"foobar"}]