Как преобразовать в объект даты и времени Python с JSON.loads? - PullRequest
28 голосов
/ 09 января 2012

У меня есть строковое представление объекта 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 во что-то вроде этого:вызывать json.loads?

Ответы [ 7 ]

21 голосов
/ 24 мая 2012

Мое решение на данный момент:

>>> 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.

16 голосов
/ 01 мая 2013

Вам необходимо передать object_hook .Из документации :

object_hook является необязательной функцией, которая будет вызываться с результатом декодирования любого литерала объекта (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 голосов
/ 08 ноября 2016

Я бы сделал то же самое, что и Никола , предложенный с 2 ​​изменениями:

  1. Используйте dateutil.parser вместо datetime.datetime.strptime
  2. Явно определите, какие исключения яхочу поймать.Я вообще рекомендую любой ценой избегать наличия пустого 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)
3 голосов
/ 09 января 2012

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

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

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

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

2 голосов
/ 15 сентября 2016

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

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.грузы ():

json_data = '{"token": "faUIO/389KLDLA", "created_at": "2016-09-15T09:54:20.564"}'
data_dictionary = json.loads(json_data, object_hook=date_hook)
1 голос
/ 09 января 2012

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

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

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

0 голосов
/ 06 сентября 2017

Вдохновленный Никольским ответом и адаптированным к 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 / исключением.В тестовом коде 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}

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

Для преобразования строки, сохраненной вisoformat (поэтому хранится в микросекундах) обратно к объекту datetime, см. этот вопрос .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...