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.
Deserialize JSON into a class
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
Using SimpleNamespace
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.
Performance measurement
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