If you’ve parsed JSON with Ruby, you’re undoubtedly familiar with the following code:

data = JSON.parse(File.read('path/to/my.json'))

This does exactly what you expect it to do. It reads the contents of path/to/my.json as text, and parses that JSON data into a Ruby Hash, which is fine for many use cases. However, you may be drawn to OpenStruct as an alternative (for any number of reasons).

Lets try it out. OpenStruct’s initializer accepts a Hash, so we should simply be able to pass in data.

require 'ostruct'
data = JSON.parse(File.read('path/to/my.json'))
struct = OpenStruct.new(data)

This will work just fine… if you don’t have nested objects. Consider the following JSON:

{
  "name": "John Smith",
  "age": 50,
  "education": {
    "school": "Stanford University",
    "degree": "BS in Computer Science"
  }
}

Running that through our previous code, we’d get an OpenStruct with the name, age, and education keys set. name would be a String, age would be a FixNum, but education? Well, we’d expect it to be an OpenStruct. But unfortunately, its a Hash. The OpenStruct initializer does not recursively convert nested Hashes to nested OpenStruct objects.

So, how do we get what we want? Enter object_class. We can get the desired effect in less lines of code with the following:

require 'ostruct'
data = JSON.parse(File.read('path/to/my.json'), object_class: OpenStruct)

The object_class option to JSON#parse specifies the dictionary-like class to use as the Ruby representation of a JSON object. In this example I used OpenStruct, but it can be a plain old Struct, or even an ActiveSupport::HashWithIndifferentAccess. You can also specify an array_class, which is an Enumerable like class that JSON will use as the Ruby representation of a JSON array.