How to convert JSON data into a Python object?
UPDATE
With Python3, you can do it in one line, using SimpleNamespace
and object_hook
:
import jsonfrom types import SimpleNamespacedata = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'# Parse JSON into an object with attributes corresponding to dict keys.x = json.loads(data, object_hook=lambda d: SimpleNamespace(**d))print(x.name, x.hometown.name, x.hometown.id)
OLD ANSWER (Python2)
In Python2, you can do it in one line, using namedtuple
and object_hook
(but it's very slow with many nested objects):
import jsonfrom collections import namedtupledata = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'# Parse JSON into an object with attributes corresponding to dict keys.x = json.loads(data, object_hook=lambda d: namedtuple('X', d.keys())(*d.values()))print x.name, x.hometown.name, x.hometown.id
or, to reuse this easily:
def _json_object_hook(d): return namedtuple('X', d.keys())(*d.values())def json2obj(data): return json.loads(data, object_hook=_json_object_hook)x = json2obj(data)
If you want it to handle keys that aren't good attribute names, check out namedtuple
's rename
parameter.
Check out the section titled Specializing JSON object decoding in the json
module documentation. You can use that to decode a JSON object into a specific Python type.
Here's an example:
class User(object): def __init__(self, name, username): self.name = name self.username = usernameimport jsondef object_decoder(obj): if '__type__' in obj and obj['__type__'] == 'User': return User(obj['name'], obj['username']) return objjson.loads('{"__type__": "User", "name": "John Smith", "username": "jsmith"}', object_hook=object_decoder)print type(User) # -> <type 'type'>
Update
If you want to access data in a dictionary via the json module do this:
user = json.loads('{"__type__": "User", "name": "John Smith", "username": "jsmith"}')print user['name']print user['username']
Just like a regular dictionary.
This is not code golf, but here is my shortest trick, using types.SimpleNamespace
as the container for JSON objects.
Compared to the leading namedtuple
solution, it is:
- probably faster/smaller as it does not create a class for each object
- shorter
- no
rename
option, and probably the same limitation on keys that are not valid identifiers (usessetattr
under the covers)
Example:
from __future__ import print_functionimport jsontry: from types import SimpleNamespace as Namespaceexcept ImportError: # Python 2.x fallback from argparse import Namespacedata = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'x = json.loads(data, object_hook=lambda d: Namespace(**d))print (x.name, x.hometown.name, x.hometown.id)