Подтвердить что ты не робот

Как преобразовать объект Python datetime с помощью JSON.loads?

У меня есть строковое представление объекта JSON.

dumped_dict = '{"debug": false, "created_at": "2020-08-09T11:24:20"}'

Когда я вызываю json.loads с этим объектом,

json.loads(dumped_dict)

Я получаю:

{'created_at': '2020-08-09T11:24:20', 'debug': False}

Здесь нет ничего плохого. Тем не менее, я хочу знать, есть ли способ конвертировать вышеуказанный объект с json.loads в нечто вроде этого:

{'created_at': datetime.datetime(2020, 08, 09, 11, 24, 20), 'debug': False}

В скором времени мы можем преобразовать строки datetime в фактические объекты datetime.datetime, в то время как вызов json.loads?

4b9b3361

Ответ 1

Мое решение:

>>> json_string = '{"last_updated": {"$gte": "Thu, 1 Mar 2012 10:00:49 UTC"}}'
>>> dct = json.loads(json_string, object_hook=datetime_parser)
>>> dct
{u'last_updated': {u'$gte': datetime.datetime(2012, 3, 1, 10, 0, 49)}}


def datetime_parser(dct):
    for k, v in dct.items():
        if isinstance(v, basestring) and re.search("\ UTC", v):
            try:
                dct[k] = datetime.datetime.strptime(v, DATE_FORMAT)
            except:
                pass
    return dct

Дополнительная информация об использовании object_hook: Кодер и декодер JSON

В моем случае строка json поступает из запроса GET в мой REST API. Это решение позволяет прозрачно "получать дату правильно", не заставляя клиентов и пользователей префиксов жесткого кодирования, таких как __date__, в JSON, если входная строка соответствует DATE_FORMAT, которая:

DATE_FORMAT = '%a, %d %b %Y %H:%M:%S UTC'

Шаблон регулярного выражения, вероятно, должен быть дополнительно уточнен

PS: в случае, если вам интересно, json_string - это запрос MongoDB/PyMongo.

Ответ 2

Вам нужно передать object_hook. Из документа :

object_hook - это необязательная функция, которая будет вызываться с помощью результат любого объектного литерала декодированного (a dict). Возвращаемое значение object_hook будет использоваться вместо dict.

Вот так:

import datetime
import json

def date_hook(json_dict):
    for (key, value) in json_dict.items():
        try:
            json_dict[key] = datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S")
        except:
            pass
    return json_dict

dumped_dict = '{"debug": false, "created_at": "2020-08-09T11:24:20"}'
loaded_dict = json.loads(dumped_dict, object_hook=date_hook)

Если вы также хотите обрабатывать часовые пояса, вам придется использовать dateutil вместо strptime.

Ответ 3

Я бы сделал то же самое, что Nicola предложил с двумя изменениями:

  • Используйте dateutil.parser вместо datetime.datetime.strptime
  • Определить, какие исключения я хочу поймать. Я вообще рекомендую избегать любой ценой пустого except:

Или в коде:

import dateutil.parser

def datetime_parser(json_dict):
    for (key, value) in json_dict.items():
        try:
            json_dict[key] = dateutil.parser.parse(value)
        except (ValueError, AttributeError):
            pass
    return json_dict

str = "{...}"  # Some JSON with date
obj = json.loads(str, object_hook=datetime_parser)
print(obj)

Ответ 4

Способ, которым задан ваш вопрос, не указывает на json, что строка является значением даты. Это отличается от документации json, которая имеет пример строки:

'{"__complex__": true, "real": 1, "imag": 2}'

В этой строке есть индикатор "__complex__": true, который может использоваться для вывода типа данных, но если нет такого индикатора, строка является просто строкой, и все, что вы можете сделать, - это регулярное перемещение по всем строки и решить, выглядят ли они как даты.

В вашем случае вы обязательно должны использовать схему, если она доступна для вашего формата.

Ответ 5

Вы можете использовать регулярное выражение, чтобы определить, хотите ли вы преобразовать определенное поле в datetime следующим образом:

def date_hook(json_dict):
    for (key, value) in json_dict.items():
        if type(value) is str and re.match('^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d*$', value):
            json_dict[key] = datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f")
        elif type(value) is str and re.match('^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$', value):
            json_dict[key] = datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S")
        else:
            pass

    return json_dict

Затем вы можете ссылаться на функцию date_hook, используя параметр object_hook в своем вызове json.loads():

json_data = '{"token": "faUIO/389KLDLA", "created_at": "2016-09-15T09:54:20.564"}'
data_dictionary = json.loads(json_data, object_hook=date_hook)

Ответ 6

Насколько я знаю, для этого нет готового решения.

Прежде всего, решение должно учитывать json schema, чтобы правильно различать строки и даты. В какой-то степени вы можете догадаться о схеме с помощью json schema inferencer (google для json schema inferencer github), а затем исправить места, которые действительно являются datetime.

Если схема известна, должно быть довольно легко сделать функцию, которая анализирует json и заменяет представления строк с помощью datetime. Некоторое вдохновение для кода можно было бы найти из validictory продукта (и проверка схемы json также может быть хорошей идеей).

Ответ 7

Вдохновленный Nicola ответ и адаптированный к python3 (str вместо basestring):

import re
from datetime import datetime
datetime_format = "%Y-%m-%dT%H:%M:%S"
datetime_format_regex = re.compile(r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$')


def datetime_parser(dct):
    for k, v in dct.items():
        if isinstance(v, str) and datetime_format_regex.match(v):
            dct[k] = datetime.strptime(v, datetime_format)
    return dct

Это позволяет избежать использования механизма try/except. В тестовом коде OP:

>>> import json
>>> json_string = '{"debug": false, "created_at": "2020-08-09T11:24:20"}'
>>> json.loads(json_string, object_hook=datetime_parser)
{'created_at': datetime.datetime(2020, 8, 9, 11, 24, 20), 'debug': False}

Переменные регулярных выражений и datetime_format могут быть легко адаптированы для соответствия другим шаблонам, например. без T в середине.

Чтобы преобразовать строку, сохраненную в isoformat (поэтому сохраненную с микросекундами), обратно к объекту datetime, обратитесь к этому вопросу.

Ответ 8

Метод реализует рекурсивный поиск строк в формате даты и времени

import json
from dateutil.parser import parse

def datetime_parser(value):
    if isinstance(value, dict):
        for k, v in value.items():
            value[k] = datetime_parser(v)
    elif isinstance(value, list):
        for index, row in enumerate(value):
            value[index] = datetime_parser(row)
    elif isinstance(value, str) and value:
        try:
            value = parse(value)
        except (ValueError, AttributeError):
            pass
    return value

json_to_dict = json.loads(YOUR_JSON_STRING, object_hook=datetime_parser)