Як подолати “datetime.datetime не серійний JSON”?


740

У мене основний диктат наступним чином:

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
Це конкретно пітон взагалі чи, можливо, джанго?
jdi

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

можливий дублікат часу дати JSON між Python та JavaScript
jdi

Я використовую mongoengine, але якщо у Пімонго є кращі способи подолати це чи подолати це, будь ласка, скажіть.
Роландо

3
Пов'язане питання по суті говорить вам не намагатися серіалізувати об'єкт дати, а скоріше перетворити його на рядок у загальному форматі 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)

Джанго

Django пропонує вроджений 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
)

Це Джанго позбавляє трохи даних:

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

Таким чином, вам може знадобитися бути обережними щодо цього в деяких випадках.


3
Чи корисно / погано застосовувати змішування декількох бібліотек, тобто мати монгоенжейн для вставки документів та пімонго для запиту / пошуку?
Роландо

Це не погана практика, вона просто передбачає деяку залежність від бібліотек, якими користується ваша основна бібліотека. Якщо ви не можете виконати те, що вам потрібно від монго-інженера, тоді ви переходите до пімонго. Це те саме з Django MongoDB. З часом пізніше ви намагатиметеся залишатися в межах джанго ORM, щоб підтримувати агностичний стан. Але іноді ви не можете зробити те, що вам потрібно в абстракції, тому ви опускаєте шар. У цьому випадку це абсолютно не пов'язане з вашою проблемою, оскільки ви просто використовуєте корисні методи для супроводу формату JSON.
jdi

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

2
@amit Це не стільки в запам’ятовуванні синтаксису, скільки в тому, щоби добре читати документацію і зберігати достатню кількість інформації в голові, щоб визнати, де і коли мені потрібно знову знайти її. У цьому випадку можна сказати «Ой власний об’єкт з json», а потім швидко оновити це використання
jdi

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

617

Мій швидкий і брудний відвал JSON, який їсть дати і все:

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

14
Це дивовижно, Але, на жаль, я не зрозумів, що сталося? Хтось може пояснити цю відповідь?
Кішор Павар

62
@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
так, правильна відповідь, більш симпатичний час імпорту, і якщо isin substance (obj, datetime.datetime), я втратив багато часу, тому що не використовував з datetime імпорту datetime, все одно спасибі
Sérgio

12
але це не пояснює, як десериалізувати його правильним типом, чи не так?
BlueTrin

2
Ні, @BlueTrin, нічого про це не сказав. У моєму випадку я десеріалізую JavaScript, який працює поза коробкою.
jgbarah

1
Це призведе до несподіваної поведінки, якщо модуль json коли-небудь оновлюватиметься, щоб включити серіалізацію об'єктів дати.
Джастін

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 має бути правильним шляхом
к.

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
Проблема полягає в тому, що у вас є багато об'єктів дати, глибоко вбудованих у структуру даних, або вони випадкові. Це не надійний метод.
Ребс

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

13
Незважаючи на те, що вона ігнорує інформацію про часовий пояс. Майте на увазі, що .now()використовує місцевий час, не вказуючи цього. Принаймні, .utcnow()слід використовувати (а потім додавати +0000 або Z)
Daniel F

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

79

Для інших, хто не потребує або хоче використовувати для цього бібліотеку пімонго .. Ви можете легко досягти перетворення JSON datetime за допомогою цього маленького фрагмента:

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
Тому що obj може не бути об'єктом [час, дата, дата]
Rebs

2
ваш приклад невірний, якщо в локальному часовому поясі встановлено ненульове зміщення UTC (більшість з них). datetime.now()повертає місцевий час (як наївний об'єкт дати), але ваш код припускає, що 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

@Natim це рішення найкраще. +1
Сувік Рей

20

У мене є додаток з подібним випуском; мій підхід полягав у тому, щоб JSONize значення дати як 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 ()? Це добре для мене.
кодування

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
Ви повинні використовувати isin substance (o, (datetime.date, datetime.datetime,)). Напевно, не завадить включити datetime.time теж.
Ребс

Я вже не думаю, що це хороше рішення. Можливо, конверсії повинні займати більш привілейоване місце - а також більш зрозуміле місце - у вашому коді, тож ви знаєте, до чого перетворюєтесь, коли ви поміщаєте речі в базу даних, або що інше, замість того, щоб все робив прозора функція. Але я не знаю.
fiatjaf

1
JSON хороший для серіалізації даних для подальшої обробки. Ви можете точно не знати, що це за дані. І вам не потрібно. Серіалізація JSON повинна просто працювати. Так само, як і перетворення unicode в ascii. Нездатність Python робити це без незрозумілих функцій робить прикро використовувати. Перевірка баз даних є окремим питанням IMO.
Ребс

Ні, це не повинно "просто працювати". Якщо ви не знаєте, як відбулася серіалізація і має отримати доступ до даних пізніше з іншої програми / мови, ви втратите.
fiatjaf

2
JSON зазвичай використовується для рядків, ints, floats, дати (я впевнений, що інші використовують валюту, температуру, зазвичай). Але timetime є частиною стандартної бібліотеки і має підтримувати де / серіалізацію. Якби не це питання, я б все-таки вручну шукав мої неймовірно складні краплі json (для яких я не завжди створював структуру) для дат і серіалізував їх 1 на 1.
Rebs

16

Цей Q повторюється знову і знову - простий спосіб виправити модуль json таким чином, щоб серіалізація підтримувала час дати.

import json
import datetime

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

Чим використовуйте серіалізацію json, як завжди, - на цей раз із датою, що серіалізується як ізоформат.

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

Результат: '{"створено": "2015-08-26T14: 21: 31.853855"} "

Докладніші відомості та слова обережності див. На: StackOverflow: Час дати JSON між Python та JavaScript


Патч для мавп FTW. Суворий факт, звичайно, що це змінює поведінку модуля json у всій вашій програмі, що може здивувати інших у великому застосуванні, тому, як правило, їх слід обережно використовувати.
Jaap Versteegh

15

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

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

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

14

якщо ви використовуєте 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

Ось просте вирішення проблеми, що надходить "серіялізація дати, що не є JSON", не може бути встановлена ​​".

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)

Вихід: -> {"дата": "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, з яким 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 на дані аргументи із застосуванням / json mimetype.

Щоб імітувати цю поведінку за допомогою 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}

Це дозволило мені імпортувати та експортувати рядки, ints, floats та об'єкти datetime. Розширення для інших типів не повинно бути важким.


1
Він вибухає в Python 3 с TypeError: 'str' does not support the buffer interface. Саме через 'wb'відкритий режим має бути 'w'. Це також спричиняє дезаріалізацію, коли у нас є дані, схожі на дату на зразок, '0000891618-05-000338'але не збігаються.
омікрон

2

перетворити date в string

date = str(datetime.datetime(somedatetimehere)) 

Відповідь jjmontes робить саме це, але без необхідності робити це чітко для кожної дати ...
bluesummers

2

Як правило, існує кілька способів серіалізації часу, наприклад:

  1. Рядок ISO, короткий і може містити інформацію про часовий пояс, наприклад, відповідь @ jgbarah
  2. Часова позначка (дані часового поясу втрачені), наприклад, відповідь @ JayTaylor
  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))

Не слід імпортувати дату як

from datetime import datetime

оскільки eval буде скаржитися. Або ви можете передати datetime як параметр eval. У будь-якому випадку це має спрацювати.


0

Я стикався з тією ж проблемою, коли екстерналізував об'єкт моделі джанго, щоб скинути, як 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

Цей супербісон бібліотеки може це зробити. Ви можете легко налаштувати серіалізатор 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.