Как преодолеть «datetime.datetime не JSON сериализуемый»?


743

У меня есть основные слова:

sample = {}
sample['title'] = "String"
sample['somedate'] = somedatetimehere

Когда я пытаюсь сделать jsonify(sample) я получаю:

TypeError: datetime.datetime(2012, 8, 8, 21, 46, 24, 862000) is not JSON serializable

Что я могу сделать, чтобы мой пример словаря смог преодолеть ошибку, описанную выше?

Примечание. Хотя это может быть неактуально, словари создаются на основе поиска записей, из mongodbкоторых, когда я распечатываю str(sample['somedate']), вывод получается 2012-08-08 21:46:24.862000.


1
Это конкретно Python в целом, или, возможно, Django?
JDI

1
Технически это определенно python, я не использую django, а извлекаю записи из mongodb.
Роландо


Я использую mongoengine, но если у pymongo есть лучшие способы обойти это или преодолеть это, пожалуйста, сообщите.
Роландо

3
Связанный вопрос, по сути, говорит вам не пытаться сериализовать объект datetime, а вместо этого преобразовать его в строку в общем формате ISO перед сериализацией.
Томас Келли

Ответы:


377

Обновлено для 2018

Исходный ответ соответствовал тому, как поля даты в MongoDB были представлены как:

{"$date": 1506816000000}

Если вы хотите универсальное решение Python для сериализации datetimeв json, посмотрите ответ @jjmontes для быстрого решения, которое не требует никаких зависимостей.


Поскольку вы используете mongoengine (для комментариев), а pymongo является зависимостью, pymongo имеет встроенные утилиты, помогающие с сериализацией json:
http://api.mongodb.org/python/1.10.1/api/bson/json_util.html

Пример использования (сериализация):

from bson import json_util
import json

json.dumps(anObject, default=json_util.default)

Пример использования (десериализация):

json.loads(aJsonString, object_hook=json_util.object_hook)

Джанго

Джанго обеспечивает родной DjangoJSONEncoder сериализатор, который правильно обрабатывает этот вид.

См. Https://docs.djangoproject.com/en/dev/topics/serialization/#djangojsonencoder.

from django.core.serializers.json import DjangoJSONEncoder

return json.dumps(
  item,
  sort_keys=True,
  indent=1,
  cls=DjangoJSONEncoder
)

Одно отличие, которое я заметил между DjangoJSONEncoderи использованием кастомов defaultвроде этого:

import datetime
import json

def default(o):
    if isinstance(o, (datetime.date, datetime.datetime)):
        return o.isoformat()

return json.dumps(
  item,
  sort_keys=True,
  indent=1,
  default=default
)

Это Django отбирает немного данных:

 "last_login": "2018-08-03T10:51:42.990", # DjangoJSONEncoder 
 "last_login": "2018-08-03T10:51:42.990239", # default

Таким образом, в некоторых случаях вам может потребоваться быть осторожным с этим.


3
Является ли хорошей / плохой практикой смешивать несколько библиотек, то есть иметь mongoengine для вставки документов и pymongo для запроса / поиска?
Роландо

Это неплохая практика, это просто подразумевает некоторую зависимость от библиотек, которые использует ваша основная библиотека. Если вы не можете выполнить то, что вам нужно от mongoengine, то вы переходите на pymongo. Это то же самое с Django MongoDB. В последнем случае вы пытаетесь оставаться в рамках django ORM, чтобы поддерживать независимое состояние бэкенда. Но иногда вы не можете делать то, что вам нужно в абстракции, поэтому вы опускаете слой. В этом случае это совершенно не связано с вашей проблемой, поскольку вы просто используете служебные методы для сопровождения формата JSON.
JDI

Я пытаюсь это сделать с помощью Flask, и кажется, что с помощью json.dump я не могу поместить оболочку jsonify () так, чтобы она возвращалась в application / json. Попытка сделать возврат jsonify (json.dumps (sample, default = json_util.default))
Роландо

2
@amit Дело не столько в запоминании синтаксиса, сколько в умении читать документацию и хранить достаточно информации в моей голове, чтобы понять, где и когда мне нужно снова ее получить. В этом случае можно сказать «О, пользовательский объект с json», а затем быстро обновить это использование
jdi

2
@guyskk Я не отслеживал изменения в bjson или mongo с тех пор, как написал это 5 лет назад. Но если вы хотите контролировать сериализацию даты и времени, вам нужно написать собственную функцию-обработчик по умолчанию, как показано в ответе jgbarah
jdi

619

Мой быстрый и грязный дамп JSON, который ест даты и все:

json.dumps(my_dictionary, indent=4, sort_keys=True, default=str)

14
Это круто, но, к сожалению, я не поняла, что случилось? Кто-нибудь может объяснить этот ответ?
Кишор Павар

63
@KishorPawar: defaultэто функция, применяемая к объектам, которые не сериализуются. В этом случае он strпросто конвертирует все, что он не знает, в строки. Который отлично подходит для сериализации, но не настолько хорош при десериализации (отсюда и «быстрая и грязная»), так как все может быть строкой без предупреждения, например, функция или массив элементов.
Марк

1
@ Марка потрясающая. Спасибо. Полезно, когда вы знаете тип этих несериализуемых значений, таких как даты.
Кишор Павар

2
Почему я всю жизнь не знал этого? :)
Арель

1
@jjmontes, не работает для всех, например, json.dumps({():1,type(None):2},default=str)поднимает TypeError, не может иметь тип или кортеж.
alancalvitti

443

Опираясь на другие ответы, простое решение , основанное на определенном сериализатором , что только новообращенные datetime.datetimeи datetime.dateобъекты в строки.

from datetime import date, datetime

def json_serial(obj):
    """JSON serializer for objects not serializable by default json code"""

    if isinstance(obj, (datetime, date)):
        return obj.isoformat()
    raise TypeError ("Type %s not serializable" % type(obj))

Как видно, код просто проверяет, является ли объект классом datetime.datetimeили datetime.date, а затем использует его .isoformat()для создания его сериализованной версии в соответствии с форматом ISO 8601, YYYY-MM-DDTHH: MM: SS (который легко декодируется с помощью JavaScript ). Если искать более сложные сериализованные представления, можно использовать другой код вместо str () (см. Другие ответы на этот вопрос для примеров). Код заканчивается созданием исключения, чтобы иметь дело с случаем, когда он вызывается с несериализуемым типом.

Эта функция json_serial может использоваться следующим образом:

from datetime import datetime
from json import dumps

print dumps(datetime.now(), default=json_serial)

Подробную информацию о том, как работает параметр по умолчанию для json.dumps, можно найти в разделе «Основное использование» документации модуля json .


5
да, правильный ответ, более красивый импорт datetime и, если isinstance (obj, datetime.datetime), я потерял много времени, потому что не использовал datetime import datetime, так или иначе, спасибо
Sérgio

12
но это не объясняет, как десериализовать его с правильным типом, не так ли?
BlueTrin

2
Нет, @BlueTrin, об этом ничего не сказано. В моем случае я десериализирую в JavaScript, который работает из коробки.
Джгбара

1
Это приведет к неожиданному поведению, если модуль json когда-либо обновится, чтобы включить сериализацию объектов datetime.
Джастин

1
@serg Но конвертирование времени в UTC будет унифицированным 01:00:00+01:00и 02:00:00+00:00не должно быть одинаковым, в зависимости от контекста. Конечно, они ссылаются на один и тот же момент времени, но смещение может быть важным аспектом значения.
Альфей

211

Я только что столкнулся с этой проблемой, и мое решение заключается в подклассе json.JSONEncoder:

from datetime import datetime
import json

class DateTimeEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, datetime):
            return o.isoformat()

        return json.JSONEncoder.default(self, o)

В вашем вызове сделать что - то вроде: json.dumps(yourobj, cls=DateTimeEncoder)The .isoformat()я получил от одного из вышеуказанных ответов.


22
повысилась, потому что реализация собственного JSONEncoder должна быть правильным способом
3k-

25
Это должно быть не только лучшим ответом, но и частью обычного кодера json. Если бы только декодирование было менее двусмысленным ..
Joost

4
Для тех, кто использует Django, смотрите DjangoJSONEncoder. docs.djangoproject.com/en/dev/topics/serialization/…
С. Кирби

4
Супер полезно. Последняя строка может бытьreturn super(DateTimeEncoder, self).default(o)
Боб Стейн

16
В Python 3 последняя строка еще проще:return super().default(o)
ariddell

124

Преобразовать дату в строку

sample['somedate'] = str( datetime.utcnow() )

10
И как я могу десериализовать это в Python?
wobmene

62
Проблема в том, что если у вас есть много объектов datetime, глубоко встроенных в структуру данных, или они случайные. Это не надежный метод.
Ребс

3
десериализовать oDate = datetime.datetime.strptime(sDate, '%Y-%m-%d %H:%M:%S.%f'). Форматы получены из: docs.python.org/2/library/datetime.html
Roman

13
Понижено, поскольку это игнорирует информацию о часовом поясе. Имейте в виду, что .now()использует местное время, не указывая этого. По крайней мере, .utcnow()следует использовать (а затем добавить +0000 или Z)
Даниэль Ф

1
@DanielF At least .utcnow() should be usedНе совсем, datetime.now(timezone.utc)рекомендуется, см. Предупреждение в: docs.python.org/3.8/library/… .
Toreno96

79

Для тех, кто не нуждается или не хочет использовать библиотеку pymongo для этого ... вы можете легко выполнить преобразование JSON даты и времени с помощью этого небольшого фрагмента:

def default(obj):
    """Default JSON serializer."""
    import calendar, datetime

    if isinstance(obj, datetime.datetime):
        if obj.utcoffset() is not None:
            obj = obj - obj.utcoffset()
        millis = int(
            calendar.timegm(obj.timetuple()) * 1000 +
            obj.microsecond / 1000
        )
        return millis
    raise TypeError('Not sure how to serialize %s' % (obj,))

Тогда используйте это так:

import datetime, json
print json.dumps(datetime.datetime.now(), default=default)

вывод: 

'1365091796124'

1
Не должно millis=быть отступом внутри оператора if? Также, вероятно, лучше использовать str (obj) для получения формата ISO, который, я думаю, более распространен.
Ребс

Почему вы хотите, чтобы он был отступ? Этот фрагмент кода работает, и полученный результат может быть легко десериализован / проанализирован из JavaScript.
Джей Тейлор

5
Потому что объект не может быть объектом [время, дата, дата / время]
Ребс

2
Ваш пример неверен, если местный часовой пояс имеет ненулевое смещение UTC (большинство из них). datetime.now()возвращает местное время (как наивный объект datetime), но ваш код предполагает, что он objнаходится в UTC, если он не учитывает часовой пояс. Используйте datetime.utcnow()вместо этого.
Jfs

1
Исправлено, чтобы вызвать ошибку типа, если obj не распознан в соответствии с рекомендацией документации Python на docs.python.org/2/library/json.html#basic-usage .
Джей Тейлор,

40

Вот мое решение:

# -*- coding: utf-8 -*-
import json


class DatetimeEncoder(json.JSONEncoder):
    def default(self, obj):
        try:
            return super(DatetimeEncoder, obj).default(obj)
        except TypeError:
            return str(obj)

Тогда вы можете использовать это так:

json.dumps(dictionnary, cls=DatetimeEncoder)

согласна. Намного лучше, по крайней мере, вне контекста mongodb. Вы можете сделать это isinstance(obj, datetime.datetime)в TypeError, добавить больше типов для обработки и в конце добавить str(obj)или repr(obj). И все ваши свалки могут просто указывать на этот специализированный класс.
JL Peyret

@ Натим это решение самое лучшее. +1
Souvik Ray

20

У меня есть приложение с похожей проблемой; мой подход состоял в том, чтобы JSONize значение datetime в виде списка из 6 элементов (год, месяц, день, час, минуты, секунды); Вы можете перейти к микросекундам в виде списка из 7 пунктов, но мне не нужно было:

class DateTimeEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            encoded_object = list(obj.timetuple())[0:6]
        else:
            encoded_object =json.JSONEncoder.default(self, obj)
        return encoded_object

sample = {}
sample['title'] = "String"
sample['somedate'] = datetime.datetime.now()

print sample
print json.dumps(sample, cls=DateTimeEncoder)

производит:

{'somedate': datetime.datetime(2013, 8, 1, 16, 22, 45, 890000), 'title': 'String'}
{"somedate": [2013, 8, 1, 16, 22, 45], "title": "String"}

Не работает, если сэкономленное время сохраняется с помощью datetime.utcnow ()
saurshaz

1
Какую ошибку вы видите с datetime.utcnow ()? Это работает хорошо для меня.
codingatty

17

Мое решение (я думаю, с меньшим количеством слов):

def default(o):
    if type(o) is datetime.date or type(o) is datetime.datetime:
        return o.isoformat()

def jsondumps(o):
    return json.dumps(o, default=default)

Тогда используйте jsondumpsвместо json.dumps. Он напечатает:

>>> jsondumps({'today': datetime.date.today()})
'{"today": "2013-07-30"}'

Если хотите, позже вы можете добавить к этому другие особые случаи, просто изменив defaultметод. Пример:

def default(o):
    if type(o) is datetime.date or type(o) is datetime.datetime:
        return o.isoformat()
    if type(o) is decimal.Decimal:
        return float(o)

1
Вы должны использовать isinstance (o, (datetime.date, datetime.datetime,)). Наверное, не мешало бы включить datetime.time.
Ребс

Я не думаю, что это хорошее решение больше. Вероятно, преобразования должны занять более привилегированное место - и также более понятное место - в вашем коде, чтобы вы знали, во что вы конвертируете, когда помещаете вещи в базу данных или что-то еще, вместо того, чтобы все делать прозрачная функция. Но я не знаю
Пятница

1
JSON хорош для сериализации данных для последующей обработки. Вы можете не знать точно, что это за данные. И тебе не нужно этого делать. Сериализация JSON должна просто работать. Так же, как преобразование Unicode в ASCII следует. Неспособность Python сделать это без непонятных функций делает его раздражающим в использовании. Проверка базы данных является отдельной проблемой IMO.
Ребс

Нет, это не должно "просто работать". Если вы не знаете, как произошла сериализация, и вам нужен доступ к данным позже из другой программы / языка, то вы потерялись.
Пятница

2
JSON обычно используется для строк, целых чисел, чисел с плавающей точкой, дат (я уверен, что другие используют валюту, температуры, как правило, тоже). Но datetime является частью стандартной библиотеки и должен поддерживать де / сериализацию. Если бы не этот вопрос, я бы по-прежнему вручную искал в моих невероятно сложных каплях json (для которых я не всегда создавал структуру) даты и сериализировал их 1 к 1.
Ребс

16

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

import json
import datetime

json.JSONEncoder.default = lambda self,obj: (obj.isoformat() if isinstance(obj, datetime.datetime) else None)

Чем использовать сериализацию JSON, как вы всегда делаете - на этот раз с датой и временем сериализуется как isoformat.

json.dumps({'created':datetime.datetime.now()})

В результате: '{"созданный": "2015-08-26T14: 21: 31.853855"}'

Более подробную информацию и несколько слов предостережения смотрите по адресу: StackOverflow: JSON datetime между Python и JavaScript


Обезьяна патч FTW. Конечно, неприятно то, что это изменяет поведение модуля json во всем приложении, что может удивить других в большом приложении, поэтому его следует использовать с осторожностью imho.
Яап Верстег,

15

Метод json.dumps может принимать необязательный параметр с именем default, который, как ожидается, будет функцией. Каждый раз, когда JSON пытается преобразовать значение, он не знает, как его преобразовать, будет вызывать функцию, которую мы передали ему. Функция получит рассматриваемый объект, и ожидается, что она вернет JSON-представление объекта.

def myconverter(o):
 if isinstance(o, datetime.datetime):
    return o.__str__()

print(json.dumps(d, default = myconverter)) 

15

если вы используете python3.7, то лучшим решением будет использование datetime.isoformat()и datetime.fromisoformat(); они работают как с наивными, так и с осознанными datetimeобъектами:

#!/usr/bin/env python3.7

from datetime import datetime
from datetime import timezone
from datetime import timedelta
import json

def default(obj):
    if isinstance(obj, datetime):
        return { '_isoformat': obj.isoformat() }
    return super().default(obj)

def object_hook(obj):
    _isoformat = obj.get('_isoformat')
    if _isoformat is not None:
        return datetime.fromisoformat(_isoformat)
    return obj

if __name__ == '__main__':
    #d = { 'now': datetime(2000, 1, 1) }
    d = { 'now': datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=-8))) }
    s = json.dumps(d, default=default)
    print(s)
    print(d == json.loads(s, object_hook=object_hook))

вывод:

{"now": {"_isoformat": "2000-01-01T00:00:00-08:00"}}
True

если вы используете python3.6 или ниже, и вас интересует только значение времени (не часовой пояс), то вы можете использовать datetime.timestamp()и datetime.fromtimestamp()вместо;

если вы используете python3.6 или ниже и вам небезразличен часовой пояс, то вы можете получить его через datetime.tzinfo, но вы должны сами сериализовать это поле; самый простой способ сделать это - добавить еще одно поле _tzinfoв сериализованный объект;

наконец, остерегайтесь точности во всех этих примерах;


datetime.isoformat () также присутствует в Python 2.7: docs.python.org/2/library/…
Powlo

11

Вы должны использовать .strftime()метод на .datetime.now()методе, чтобы сделать его как сериализуемый метод.

Вот пример:

from datetime import datetime

time_dict = {'time': datetime.now().strftime('%Y-%m-%dT%H:%M:%S')}
sample_dict = {'a': 1, 'b': 2}
sample_dict.update(time_dict)
sample_dict

Вывод:

Out[0]: {'a': 1, 'b': 2, 'time': '2017-10-31T15:16:30'}

10

Вот простое решение проблемы «datetime not JSON serializable».

enco = lambda obj: (
    obj.isoformat()
    if isinstance(obj, datetime.datetime)
    or isinstance(obj, datetime.date)
    else None
)

json.dumps({'date': datetime.datetime.now()}, default=enco)

Выходные данные: -> {"date": "2015-12-16T04: 48: 20.024609"}


8

Вы должны предоставить пользовательский класс кодировщика с clsпараметром json.dumps. Цитировать из документов :

>>> import json
>>> class ComplexEncoder(json.JSONEncoder):
...     def default(self, obj):
...         if isinstance(obj, complex):
...             return [obj.real, obj.imag]
...         return json.JSONEncoder.default(self, obj)
...
>>> dumps(2 + 1j, cls=ComplexEncoder)
'[2.0, 1.0]'
>>> ComplexEncoder().encode(2 + 1j)
'[2.0, 1.0]'
>>> list(ComplexEncoder().iterencode(2 + 1j))
['[', '2.0', ', ', '1.0', ']']

В качестве примера используются комплексные числа, но вы также можете легко создать класс для кодирования дат (за исключением того, что JSON немного размыт в отношении дат)


5

Самый простой способ сделать это - изменить часть слова в формате datetime на isoformat. Это значение будет эффективно представлять собой строку в isoformat, с которой json согласен.

v_dict = version.dict()
v_dict['created_at'] = v_dict['created_at'].isoformat()

5

На самом деле это довольно просто. Если вам нужно часто сериализовать даты, то работайте с ними как со строками. При необходимости вы можете легко преобразовать их обратно в объекты даты и времени.

Если вам нужно работать в основном как объекты даты и времени, то перед сериализацией преобразуйте их в строки.

import json, datetime

date = str(datetime.datetime.now())
print(json.dumps(date))
"2018-12-01 15:44:34.409085"
print(type(date))
<class 'str'>

datetime_obj = datetime.datetime.strptime(date, '%Y-%m-%d %H:%M:%S.%f')
print(datetime_obj)
2018-12-01 15:44:34.409085
print(type(datetime_obj))
<class 'datetime.datetime'>

Как видите, вывод одинаков в обоих случаях. Только тип отличается.


3

Если вы используете результат в представлении, обязательно верните правильный ответ. Согласно API, jsonify делает следующее:

Создает ответ с JSON-представлением данных аргументов с помощью mimetype application / json.

Чтобы имитировать это поведение с помощью json.dumps, вам нужно добавить несколько дополнительных строк кода.

response = make_response(dumps(sample, cls=CustomEncoder))
response.headers['Content-Type'] = 'application/json'
response.headers['mimetype'] = 'application/json'
return response

Вы также должны вернуть диктовку, чтобы полностью повторить ответ jsonify. Итак, весь файл будет выглядеть так

from flask import make_response
from json import JSONEncoder, dumps


class CustomEncoder(JSONEncoder):
    def default(self, obj):
        if set(['quantize', 'year']).intersection(dir(obj)):
            return str(obj)
        elif hasattr(obj, 'next'):
            return list(obj)
        return JSONEncoder.default(self, obj)

@app.route('/get_reps/', methods=['GET'])
def get_reps():
    sample = ['some text', <datetime object>, 123]
    response = make_response(dumps({'result': sample}, cls=CustomEncoder))
    response.headers['Content-Type'] = 'application/json'
    response.headers['mimetype'] = 'application/json'
    return response

1
Вопрос не имеет ничего общего с колбой.
Зоран Павлович

2
Вопрос о питоне. Мой ответ решает вопрос с использованием Python. ОП не сказал, должно ли решение включать или исключать определенные библиотеки. Это также полезно для тех, кто читает этот вопрос, кому нужна альтернатива pymongo.
Reubano

Они задают вопрос как о Python, а не о Flask. Колба даже не нужна в вашем ответе на вопрос, поэтому я предлагаю вам удалить его.
Зоран Павлович

3

Попробуйте это с примером, чтобы разобрать это:

#!/usr/bin/env python

import datetime
import json

import dateutil.parser  # pip install python-dateutil


class JSONEncoder(json.JSONEncoder):

    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            return obj.isoformat()
        return super(JSONEncoder, self).default(obj)


def test():
    dts = [
        datetime.datetime.now(),
        datetime.datetime.now(datetime.timezone(-datetime.timedelta(hours=4))),
        datetime.datetime.utcnow(),
        datetime.datetime.now(datetime.timezone.utc),
    ]
    for dt in dts:
        dt_isoformat = json.loads(json.dumps(dt, cls=JSONEncoder))
        dt_parsed = dateutil.parser.parse(dt_isoformat)
        assert dt == dt_parsed
        print(f'{dt}, {dt_isoformat}, {dt_parsed}')
        # 2018-07-22 02:22:42.910637, 2018-07-22T02:22:42.910637, 2018-07-22 02:22:42.910637
        # 2018-07-22 02:22:42.910643-04:00, 2018-07-22T02:22:42.910643-04:00, 2018-07-22 02:22:42.910643-04:00
        # 2018-07-22 06:22:42.910645, 2018-07-22T06:22:42.910645, 2018-07-22 06:22:42.910645
        # 2018-07-22 06:22:42.910646+00:00, 2018-07-22T06:22:42.910646+00:00, 2018-07-22 06:22:42.910646+00:00


if __name__ == '__main__':
    test()

2

Мое решение ...

from datetime import datetime
import json

from pytz import timezone
import pytz


def json_dt_serializer(obj):
    """JSON serializer, by macm.
    """
    rsp = dict()
    if isinstance(obj, datetime):
        rsp['day'] = obj.day
        rsp['hour'] = obj.hour
        rsp['microsecond'] = obj.microsecond
        rsp['minute'] = obj.minute
        rsp['month'] = obj.month
        rsp['second'] = obj.second
        rsp['year'] = obj.year
        rsp['tzinfo'] = str(obj.tzinfo)
        return rsp
    raise TypeError("Type not serializable")


def json_dt_deserialize(obj):
    """JSON deserialize from json_dt_serializer, by macm.
    """
    if isinstance(obj, str):
        obj = json.loads(obj)
    tzone = timezone(obj['tzinfo'])
    tmp_dt = datetime(obj['year'],
                      obj['month'],
                      obj['day'],
                      hour=obj['hour'],
                      minute=obj['minute'],
                      second=obj['second'],
                      microsecond=obj['microsecond'])
    loc_dt = tzone.localize(tmp_dt)
    deserialize = loc_dt.astimezone(tzone)
    return deserialize    

Хорошо, теперь некоторые тесты.

# Tests
now = datetime.now(pytz.utc)

# Using this solution
rsp = json_dt_serializer(now)
tmp = json_dt_deserialize(rsp)
assert tmp == now
assert isinstance(tmp, datetime) == True
assert isinstance(now, datetime) == True

# using default from json.dumps
tmp = json.dumps(datetime.now(pytz.utc), default=json_dt_serializer)
rsp = json_dt_deserialize(tmp)
assert isinstance(rsp, datetime) == True

# Lets try another timezone
eastern = timezone('US/Eastern')
now = datetime.now(eastern)
rsp = json_dt_serializer(now)
tmp = json_dt_deserialize(rsp)

print(tmp)
# 2015-10-22 09:18:33.169302-04:00

print(now)
# 2015-10-22 09:18:33.169302-04:00

# Wow, Works!
assert tmp == now

2

Вот мое полное решение для преобразования даты и времени в JSON и обратно.

import calendar, datetime, json

def outputJSON(obj):
    """Default JSON serializer."""

    if isinstance(obj, datetime.datetime):
        if obj.utcoffset() is not None:
            obj = obj - obj.utcoffset()

        return obj.strftime('%Y-%m-%d %H:%M:%S.%f')
    return str(obj)

def inputJSON(obj):
    newDic = {}

    for key in obj:
        try:
            if float(key) == int(float(key)):
                newKey = int(key)
            else:
                newKey = float(key)

            newDic[newKey] = obj[key]
            continue
        except ValueError:
            pass

        try:
            newDic[str(key)] = datetime.datetime.strptime(obj[key], '%Y-%m-%d %H:%M:%S.%f')
            continue
        except TypeError:
            pass

        newDic[str(key)] = obj[key]

    return newDic

x = {'Date': datetime.datetime.utcnow(), 34: 89.9, 12.3: 90, 45: 67, 'Extra': 6}

print x

with open('my_dict.json', 'w') as fp:
    json.dump(x, fp, default=outputJSON)

with open('my_dict.json') as f:
    my_dict = json.load(f, object_hook=inputJSON)

print my_dict

Вывод

{'Date': datetime.datetime(2013, 11, 8, 2, 30, 56, 479727), 34: 89.9, 45: 67, 12.3: 90, 'Extra': 6}
{'Date': datetime.datetime(2013, 11, 8, 2, 30, 56, 479727), 34: 89.9, 45: 67, 12.3: 90, 'Extra': 6}

Файл JSON

{"Date": "2013-11-08 02:30:56.479727", "34": 89.9, "45": 67, "12.3": 90, "Extra": 6}

Это позволило мне импортировать и экспортировать строки, целые, числа с плавающей точкой и объекты даты и времени. Это не должно быть трудно расширить для других типов.


1
Взрывается в Python 3 с TypeError: 'str' does not support the buffer interface. Именно из-за 'wb'открытого режима, должно быть 'w'. Это также сказывается на десериализации, когда у нас есть данные, похожие на дату, '0000891618-05-000338'но не совпадающие по шаблону.
omikron

2

Преобразовать date в string

date = str(datetime.datetime(somedatetimehere)) 

Ответ jjmontes делает именно это, но без необходимости делать это явно для каждой даты ...
bluesummers

2

Обычно существует несколько способов сериализации datetime, например:

  1. Строка ISO, короткая и может включать информацию о часовом поясе, например, @ jgbarah's ответ
  2. Временная метка (данные о часовом поясе потеряны), например, @ JayTaylor's ответ
  3. Словарь свойств (включая часовой пояс).

Если вы согласны с последним способом, пакет json_tricks обрабатывает даты, время и даты, включая часовые пояса.

from datetime import datetime
from json_tricks import dumps
foo = {'title': 'String', 'datetime': datetime(2012, 8, 8, 21, 46, 24, 862000)}
dumps(foo)

который дает:

{"title": "String", "datetime": {"__datetime__": null, "year": 2012, "month": 8, "day": 8, "hour": 21, "minute": 46, "second": 24, "microsecond": 862000}}

Так что все, что вам нужно сделать, это

`pip install json_tricks`

а затем импортировать из json_tricksвместо json.

Преимущество не сохранения его как единственной строки, int или float возникает при декодировании: если вы встречаете только строку или особенно int или float, вам нужно знать что-то о данных, чтобы знать, является ли это датой-временем. В качестве подсказки вы можете хранить метаданные, чтобы их можно было декодировать автоматически, что json_tricksвам и нужно. Это также легко редактируется для людей.

Отказ от ответственности: это сделано мной. Потому что у меня была такая же проблема.


1

Я получил то же сообщение об ошибке при написании сериализатора внутри класса с sqlalchemy. Так что вместо:

Class Puppy(Base):
    ...
    @property
    def serialize(self):
        return { 'id':self.id,
                 'date_birth':self.date_birth,
                  ...
                }

Я просто позаимствовал идею jgbarah об использовании isoformat () и добавил исходное значение с помощью isoformat (), чтобы оно теперь выглядело так:

                  ...
                 'date_birth':self.date_birth.isoformat(),
                  ...

1

Быстрое исправление, если вы хотите собственное форматирование

for key,val in sample.items():
    if isinstance(val, datetime):
        sample[key] = '{:%Y-%m-%d %H:%M:%S}'.format(val) #you can add different formating here
json.dumps(sample)

1

Если вы находитесь по обе стороны связи, вы можете использовать функции repr () и eval () вместе с json.

import datetime, json

dt = datetime.datetime.now()
print("This is now: {}".format(dt))

dt1 = json.dumps(repr(dt))
print("This is serialised: {}".format(dt1))

dt2 = json.loads(dt1)
print("This is loaded back from json: {}".format(dt2))

dt3 = eval(dt2)
print("This is the same object as we started: {}".format(dt3))

print("Check if they are equal: {}".format(dt == dt3))

Вы не должны импортировать datetime как

from datetime import datetime

Так как Эвал будет жаловаться. Или вы можете передать datetime в качестве параметра для eval. В любом случае это должно работать.


0

Я столкнулся с той же проблемой при выводе объекта модели django в дамп как JSON. Вот как вы можете решить это.

def externalize(model_obj):
  keys = model_obj._meta.get_all_field_names() 
  data = {}
  for key in keys:
    if key == 'date_time':
      date_time_obj = getattr(model_obj, key)
      data[key] = date_time_obj.strftime("%A %d. %B %Y")
    else:
      data[key] = getattr(model_obj, key)
  return data

0
def j_serial(o):     # self contained
    from datetime import datetime, date
    return str(o).split('.')[0] if isinstance(o, (datetime, date)) else None

Использование вышеуказанной утилиты:

import datetime
serial_d = j_serial(datetime.datetime.now())
if serial_d:
    print(serial_d)  # output: 2018-02-28 02:23:15

0

Эта библиотека superjson может сделать это. И вы можете легко настроить json сериализатор для вашего собственного объекта Python, следуя этой инструкции https://superjson.readthedocs.io/index.html#extend .

Общая концепция:

ваш код должен найти правильный метод сериализации / десериализации на основе объекта python. Обычно полное имя класса является хорошим идентификатором.

И тогда ваш метод ser / deser должен быть в состоянии преобразовать ваш объект в обычный сериализуемый объект Json, комбинацию универсального типа python, dict, list, string, int, float. И реализуй свой метод дезер обратно.


-1

Я не могу на 100% правильно, но это простой способ сериализации

#!/usr/bin/python
import datetime,json

sampledict = {}
sampledict['a'] = "some string"
sampledict['b'] = datetime.datetime.now()

print sampledict   # output : {'a': 'some string', 'b': datetime.datetime(2017, 4, 15, 5, 15, 34, 652996)}

#print json.dumps(sampledict)

'''
output : 

Traceback (most recent call last):
  File "./jsonencodedecode.py", line 10, in <module>
    print json.dumps(sampledict)
  File "/usr/lib/python2.7/json/__init__.py", line 244, in dumps
    return _default_encoder.encode(obj)
  File "/usr/lib/python2.7/json/encoder.py", line 207, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib/python2.7/json/encoder.py", line 270, in iterencode
    return _iterencode(o, 0)
  File "/usr/lib/python2.7/json/encoder.py", line 184, in default
    raise TypeError(repr(o) + " is not JSON serializable")
TypeError: datetime.datetime(2017, 4, 15, 5, 16, 17, 435706) is not JSON serializable


'''

sampledict['b'] = datetime.datetime.now().strftime("%B %d, %Y %H:%M %p")

afterdump = json.dumps(sampledict)

print afterdump  #output : {"a": "some string", "b": "April 15, 2017 05:18 AM"}

print type(afterdump) #<type 'str'>


afterloads = json.loads(afterdump) 

print afterloads # output : {u'a': u'some string', u'b': u'April 15, 2017 05:18 AM'}


print type(afterloads) # output :<type 'dict'> 
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.