Как преодолеть " datetime.datetime не сериализуется JSON"?



у меня есть основной дикт следующим образом:



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.

973   27  

27 ответов:

обновлено на 2018 год

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

{"$date": 1506816000000}

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


как вы используете mongoengine (за комментарии) и pymongo является зависимостью, pymongo имеет встроенные утилиты, чтобы помочь с json serialization:
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 такой:

def default(o):
  if type(o) is datetime.date or type(o) is 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

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

основываясь на других ответах, простое решение на основе конкретного сериализатора, который просто преобразует 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.дампы работы можно найти в раздел основное использование документации модуля json.

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

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

Я только что столкнулся с этой проблемой, и мое решение-подкласс 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() я получил от одного из вышеуказанных ответов.

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

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

для тех, кто не нуждается или не хочет использовать библиотеку pymongo для этого.. вы можете легко достичь преобразования datetime 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'

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

# -*- 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)

у меня есть приложение с аналогичной проблемой; мой подход состоял в том, чтобы 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"}

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

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)

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

import json
import datetime

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

чем использовать сериализацию json, как вы всегда делаете - на этот раз с сериализацией datetime как изоформат.

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

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

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

вот простое решение для того, чтобы прийти " 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)

выход:-> {"дата": "2015-12-16T04:48:20.024609"}

вы должны предоставить пользовательский кодировщик класс

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

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

вы должны использовать .strftime() метод on .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'}

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

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

чтобы имитировать это поведение с JSON.отвалите вы должны добавить несколько дополнительных строк кода.

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

вы также должны вернуть dict для полной репликации ответа 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

вот мое полное решение для преобразования datetime в 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 File

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

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

преобразование date to string

date = str(datetime.datetime(somedatetimehere)) 

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

  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, вам нужно знать что-то о данных, чтобы знать, если это datetime. Как dict, вы можете хранить метаданные, чтобы их можно было декодировать автоматически, что и происходит json_tricks делает для вас. Он также легко редактируется для людей.

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

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

#!/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()

Если вы находитесь на обеих сторонах связи вы можете использовать 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

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

мое решение ...

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

Я получил то же сообщение об ошибке при написании serialize декоратор внутри класса с 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(),
                  ...

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

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)

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

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

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

я столкнулся с той же проблемой при экстернализации объекта модели 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
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

Я не могу 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'> 

Comments

    Ничего не найдено.