Python json object_hook


Today, I was building a REST client for one of the REST server applications using Python. I decided to use Python requests library for writing my REST API client. Requests is a very easy to use library that you can use to quickly bootstrap your REST API client. Writing REST client for REST endpoints was a matter of an hour. This REST API client will be used from our custom Jython(JVM implementation of Python) REPL. REST API has only two endpoints that return JSON objects. Response of first endpoint was fed to the second endpoint. I was returning the JSON response as Python dictionary. User can change values of the first response and pass it to the second API call. In Python, you work with dictionary as shown below.

my_dict = {"one": 1, "two": 2, "three": 3}
my_dict["one"] # return 1

We wanted to make sure users of our Jython REPL can access dictionary like object property access as shown below.

mydict.one # Should return 1

You can make Python dict support object access by writing your own dictionary extend dict. Then, you have to provide implementation of __getattr__ and __setattr__ methods as shown below.

class DictWithAttributeAccess(dict):
    def __getattr__(self, key):
        return self[key]

    def __setattr__(self, key, value):
        self[key] = value

In the code show above:
1. We created a new class DictWithAttributeAccess that extends dict.
2. __getattr__ is called when an attribute lookup has not found the attribute in the usual place. So, if attribute is not found, just look up in the dictionary.
3. __setattr__ is called when an attribute assignment is attempted. This method will be used for assignment instead of default mechanism.

If your method return DictWithAttributeAccess instead of Python dict then you can access like object.

my_dict = {"one": 1, "two": 2, "three": 3}
another_dict = DictWithAttributeAccess(my_dict)
another_dict.one # return 1

This works if you don’t have nested dictionary structure. I was using Python json module convert response into dictionary.

json.loads(response.text)

The above method converts JSON into Python dictionary. json module converts all JSON objects into dictionary. So, if we convert the top level dictionary into DictWithAttributeAccess we can’t access nested dictionaries with object access.

def parse_response(self, response):
  return DictWithAttributeAccess(json.loads(response.text))

What we wanted was to use DictWithAttributeAccess for all dictionaries even when they are nested. This can be solved by object_hook parameter of json.loads method as shown below.

def parse_response(self, response):
    return json.loads(response.text, object_hook = lambda dict: DictWithAttributeAccess(dict))

According to documentation,

object_hook is an optional function that will be called with the result of any object literal decoded (a dict). The return value of object_hook will be used instead of the dict.

One thought on “Python json object_hook”

Leave a comment