Reading JSON into a Python object

While there is nothing wrong with dicts, there are a couple of easier methods to work with JSON structures.

The obvious one is to read the json and initialize a class with the dictionary object.

The following JSON object.

{
    "oclc": 1487587,
    "title": "The lord of the rings",
    "author": "J R R Tolkien"
}

And with a matching Python class.

class Book():
    def __init__(self, oclc, title, author):
        self.oclc = oclc
        self.title = title
        self.author = author

Read the JSON string and initialize the class.

import json
book_json = '{"oclc":1487587,"title":"The lord of the rings","author":"J R R Tolkien"}'
book_dict = json.loads(book_json)
book_object = Book(**book_dict)

Now you can access the JSON data as with any Python object.

:::Python console session
>>> print(book_object.title)
The lord of the rings

With this variant there is no need to specify a class before, it constructs an object from the JSON-structure as is.

import json
from types import SimpleNamespace
book_json = '{"oclc":1487587,"title":"The lord of the rings","author":"J R R Tolkien"}'
book_object = json.loads(book_json, object_hook=lambda z: SimpleNamespace(**z))

The trick with SimpleNameSpace is actually an old is one but derived from collections.namedtuple, but IMHO this one is a little more readable.

:::Python console session
>>> print(book_object.title)
The lord of the rings

Let’s demonstrate the old method as well.

import json
from collections import namedtuple
book_json = '{"oclc":1487587,"title":"The lord of the rings","author":"J R R Tolkien"}'
book_object = json.loads(book_json, object_hook=lambda book_dict: namedtuple('Book', book_dict.keys())(*book_dict.values()))

And the result.

:::Python console session
>>> print(book_object.title)
The lord of the rings

Personally I prefer the first, in cases where JSON-structure doesn’t change. And the second where processing input is a one-shot operation.

My friend Jean-Michel suggested that I include a small benchmark test of the different methods.

import json, timeit
from collections import namedtuple
from types import SimpleNamespace
book_json = '{"oclc":1487587,"title":"The lord of the rings","author":"J R R Tolkien"}'

class Book():
 def __init__(self, oclc, title, author):
  self.oclc = oclc
  self.title = title
  self.author = author

def run1():
 book_dict = json.loads(book_json)
 Book(**book_dict)
print('-- Class')
print(timeit.timeit(stmt=run1))

def run2():
 json.loads(book_json, object_hook=lambda z: SimpleNamespace(**z))
print('-- SimpleNamespace')
print(timeit.timeit(stmt=run2))

def run3():
 json.loads(book_json, object_hook=lambda book_dict: namedtuple('Book', book_dict.keys())(*book_dict.values()))
print('-- namedtuple')
print(timeit.timeit(stmt=run3))

And the results.

-- Class
1.6462271150085144

-- SimpleNamespace
2.917828275996726

-- namedtuple
43.440509464009665