"breakfast": {"items": {"breakfast burritos": "$6.00", "pancakes":
"$4.00"}, "hours": "7-11"}}'
А теперь превратим строку JSON menu_json обратно в структуру данных (menu2) с помощью функции loads():
>>> menu2 = json.loads(menu_json)
>>> menu2
{'breakfast': {'items': {'breakfast burritos': '$6.00', 'pancakes':
'$4.00'}, 'hours': '7-11'}, 'lunch': {'items': {'hamburger': '$5.00'},
'hours': '11-3'}, 'dinner': {'items': {'spaghetti': '$8.00'}, 'hours': '3-10'}}
menu и menu2 являются словарями с одинаковыми ключами и значениями. Как всегда, в случае обычных словарей порядок, в котором вы получаете ключи, различается.
Вы можете получить исключение, пытаясь закодировать или декодировать некоторые объекты, включая такие объекты, как datetime (этот вопрос детально рассматривается в разделе «Календари и часы» главы 10), как показано здесь:
>>> import datetime
>>> now = datetime.datetime.utcnow()
>>> now
datetime.datetime(2013, 2, 22, 3, 49, 27, 483336)
>>> json.dumps(now)
Traceback (most recent call last):
#… (deleted stack trace to save trees)
TypeError: datetime.datetime(2013, 2, 22, 3, 49, 27, 483336) is not JSON serializable
>>>
Это может случиться, поскольку стандарт JSON не определяет типы даты или времени — он ожидает, что вы укажете ему, как с ними работать. Вы можете преобразовать формат datetime во что-то, что JSON понимает, вроде строки или значения времени epoch (его мы рассмотрим в главе 10):
>>> now_str = str(now)
>>> json.dumps(now_str)
'"2013-02-22 03:49:27.483336"'
>>> from time import mktime
>>> now_epoch = int(mktime(now.timetuple()))
>>> json.dumps(now_epoch)
'1361526567'
Если значение datetime встретится между нормальными сконвертированными типами данных, может быть неприятно выполнять такие особые преобразования. Вы можете изменить то, как JSON будет закодирован, с помощью наследования, что описано в разделе «Наследование» главы 6. Документация JSON для Python содержит пример такого переопределения для комплексных чисел, что также заставляет JSON притвориться мертвым. Напишем переопределение для datetime:
>>> class DTEncoder(json.JSONEncoder):
…·····def default(self, obj):
…·········# isinstance() checks the type of obj
…·········if isinstance(obj, datetime.datetime):
…·············return int(mktime(obj.timetuple()))
…·········# else it's something the normal decoder knows:
…·········return json.JSONEncoder.default(self, obj)
…
>>> json.dumps(now, cls=DTEncoder)
'1361526567'
Новый класс DTEncoder является подклассом, или классом-потомком, класса JSONEncoder. Нам нужно лишь переопределить его метод default(), добавив обработку datetime. Наследование гарантирует, что все остальное будет обработано родительским классом.
Функция isinstance() проверяет, является ли объект obj объектом класса datetime.datetime. Поскольку в Python все является объектом, функция isinstance() работает везде:
>>> type(now)
>>> isinstance(now, datetime.datetime)
True
>>> type(234)
>>> isinstance(234, int)
True
>>> type('hey')
>>> isinstance('hey', str)
True