如何克服"datetime.datetime而不是JSON可序列化"?

Rol*_*ndo 626 python json

我有一个基本的词典如下:

sample = {}
sample['title'] = "String"
sample['somedate'] = somedatetimehere
Run Code Online (Sandbox Code Playgroud)

当我尝试做的时候,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.

jjm*_*tes 479

我的快速和脏的JSON转储吃日期和所有东西:

json.dumps(my_dictionary, indent=4, sort_keys=True, default=str)
Run Code Online (Sandbox Code Playgroud)

  • @KishorPawar:`default`是一个应用于不可序列化的对象的函数.在这种情况下,它是`str`,所以它只是将它不知道的所有内容转换为字符串.这对于序列化很有用,但在反序列化时却不那么好(因此"快速和脏"),因为任何事情都可能在没有警告的情况下被字符串化,例如函数或numpy数组. (50认同)
  • 为什么我一辈子都不知道这一点。:) (25认同)
  • 这很棒,但不幸的是我不明白发生了什么?有人能解释这个答案吗? (9认同)
  • @马克棒极了。谢谢。当您知道那些不可序列化的值(如日期)的类型时很有用。 (2认同)
  • 有人可以分享一下如何返回原始词典吗? (2认同)
  • @jjmontes,并不适用于所有情况,例如 `json.dumps({():1,type(None):2},default=str)` 会引发 `TypeError`,不能有类型或元组。 (2认同)

jgb*_*rah 426

建立在其他答案的基础上,只是转换一个特定序列化一个简单的解决方案datetime.datetimedatetime.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))
Run Code Online (Sandbox Code Playgroud)

如图所示,代码只是检查以确定对象是否为类datetime.datetimedatetime.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)
Run Code Online (Sandbox Code Playgroud)

有关如何使用json.dumps的默认参数的详细信息,请参阅json模块文档的基本用法部分.

  • 但这并没有解释如何用正确的类型对其进行反序列化,不是吗? (11认同)
  • 是的正确的答案,更漂亮的导入日期时间和如果isinstance(obj,datetime.datetime),我失去了很多时间因为没有使用从datetime导入日期时间,无论如何谢谢 (5认同)
  • 不,@BlueTrin,对此没有任何评论。就我而言,我正在使用JavaScript进行反序列化,它可以直接使用。 (2认同)
  • 如果 json 模块更新以包含日期时间对象的序列化,这将导致意外行为。 (2认同)
  • @serg 但是将时间转换为 UTC 会统一 `01:00:00+01:00` 和 `02:00:00+00:00` ,它们不应该相同,具体取决于上下文。当然,它们指的是同一时间点,但偏移量可能是值的相关方面。 (2认同)

jdi*_*jdi 328

更新于2018年

最初的答案符合MongoDB"date"字段的表示方式:

{"$date": 1506816000000}

如果你想要一个通用的Python解决方案来序列化为datetimejson,请查看@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)
Run Code Online (Sandbox Code Playgroud)

用法示例(反序列化):

json.loads(aJsonString, object_hook=json_util.object_hook)
Run Code Online (Sandbox Code Playgroud)

Django的

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
)
Run Code Online (Sandbox Code Playgroud)

我注意到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
)
Run Code Online (Sandbox Code Playgroud)

这是Django剥离了一些数据:

 "last_login": "2018-08-03T10:51:42.990", # DjangoJSONEncoder 
 "last_login": "2018-08-03T10:51:42.990239", # default
Run Code Online (Sandbox Code Playgroud)

因此,在某些情况下,您可能需要小心.

  • 混合多个库是好/坏的做法,即使用mongoengine插入文档和pymongo进行查询/检索? (3认同)
  • @amit它不是为了记忆语法,而是为了善于阅读文档并在脑中存储足够的信息来识别我需要在何时何地再次检索它.在这种情况下,有人可能会说"用json自定义对象",然后快速刷新该用法 (2认同)
  • @guyskk自从我5年前写这篇文章以来,我没有跟踪过bjson或mongo的变化.但是如果你想控制日期时间的序列化,那么你需要编写自己的默认处理函数,如jgbarah给出的答案所示. (2认同)

小智 196

我刚遇到这个问题,我的解决方案是子类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)
Run Code Online (Sandbox Code Playgroud)

在您的通话做这样的事情:json.dumps(yourobj, cls=DateTimeEncoder).isoformat()我从上面的答案之一了.

  • 这不仅应该是最佳答案,这应该是常规json编码器的一部分.如果只是解码不那么模糊.. (22认同)
  • 因为实现自定义JSONEncoder应该是正确的方法 (19认同)
  • 使用Python 3,最后一行更简单:``return super().default(o)`` (11认同)
  • 对于那些使用Django的人,请参阅`DjangoJSONEncoder`.https://docs.djangoproject.com/en/dev/topics/serialization/#djangojsonencoder (4认同)
  • 超级有帮助.最后一行可以是`return super(DateTimeEncoder,self).default(o)` (4认同)
  • 最好将解码器代码也添加到您的示例中 (2认同)

D.A*_*D.A 114

将日期转换为字符串

sample['somedate'] = str( datetime.utcnow() )
Run Code Online (Sandbox Code Playgroud)

  • 问题是如果您有许多日期时间对象深深嵌入数据结构中,或者它们是随机的.这不是一种可靠的方法. (60认同)
  • 被忽视,因为它忽略了时区信息.请记住`.now()`使用本地时间,而不指明这一点.至少应该使用`.utcnow()`(然后附加+0000或Z) (12认同)
  • 我怎么能用Python反序列化呢? (10认同)
  • 反序列化:`oDate = datetime.datetime.strptime(sDate,'%Y-%m-%d%H:%M:%S.%f')`.格式来自:https://docs.python.org/2/library/datetime.html (2认同)
  • @DanielF`至少应该使用 .utcnow()` 不完全是,建议`datetime.now(timezone.utc)`,请参阅警告:https://docs.python.org/3.8/library/datetime.html #datetime.datetime.utcnow。 (2认同)

Jay*_*lor 78

对于那些不需要或不想使用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,))
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它:

import datetime, json
print json.dumps(datetime.datetime.now(), default=default)
Run Code Online (Sandbox Code Playgroud)

输出: 

'1365091796124'
Run Code Online (Sandbox Code Playgroud)

  • 因为obj可能不是[time,date,datetime]对象 (5认同)
  • 如果本地时区具有非零UTC偏移(大多数),则示例不正确.`datetime.now()`返回本地时间(作为一个天真的日期时间对象),但是如果它不是时区感知的话,你的代码假定`obj`是UTC.请改用`datetime.utcnow()`. (2认同)
  • 根据 https://docs.python.org/2/library/json.html#basic-usage 上的 Python 文档建议对其进行调整,以便在无法识别 obj 时引发类型错误。 (2认同)

Nat*_*tim 37

这是我的解决方案:

# -*- 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)
Run Code Online (Sandbox Code Playgroud)

然后你就可以这样使用它:

json.dumps(dictionnary, cls=DatetimeEncoder)
Run Code Online (Sandbox Code Playgroud)


cod*_*tty 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)
Run Code Online (Sandbox Code Playgroud)

生产:

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


fia*_*jaf 16

我的解决方案(我认为不那么冗长):

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)
Run Code Online (Sandbox Code Playgroud)

然后使用jsondumps而不是json.dumps.它将打印:

>>> jsondumps({'today': datetime.date.today()})
'{"today": "2013-07-30"}'
Run Code Online (Sandbox Code Playgroud)

我想要,稍后您可以通过简单的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)
Run Code Online (Sandbox Code Playgroud)

  • JSON 适用于序列化数据以供稍后处理。您可能不确切知道该数据是什么。你不应该需要。序列化 JSON 应该可以正常工作。就像将 unicode 转换为 ascii 应该的一样。如果没有晦涩的函数,Python 就无法做到这一点,这使得使用起来很烦人。数据库验证是一个单独的问题 IMO。 (2认同)
  • JSON通常用于字符串,整数,浮点数,日期(我确定其他人也使用货币,温度,通常也是如此).但是datetime是标准库的一部分,应该支持de/serialization.如果不是这个问题,我仍然会手动搜索我难以置信的复杂json blob(我并不总是创建结构)的日期并逐个序列化它们. (2认同)

dav*_*das 16

这个Q一次又一次地重复 - 一种修补json模块的简单方法,使序列化支持日期时间.

import json
import datetime

json.JSONEncoder.default = lambda self,obj: (obj.isoformat() if isinstance(obj, datetime.datetime) else None)
Run Code Online (Sandbox Code Playgroud)

比你经常使用json序列化 - 这次将datetime序列化为isoformat.

json.dumps({'created':datetime.datetime.now()})
Run Code Online (Sandbox Code Playgroud)

导致:'{"created":"2015-08-26T14:21:31.853855"}'

请参阅更多详细信息和注意事项: StackOverflow:Python和JavaScript之间的JSON日期时间


小智 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)
Run Code Online (Sandbox Code Playgroud)

输出: - > {"date":"2015-12-16T04:48:20.024609"}


Ben*_*ari 10

您应该使用.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
Run Code Online (Sandbox Code Playgroud)

输出:

Out[0]: {'a': 1, 'b': 2, 'time': '2017-10-31T15:16:30'}
Run Code Online (Sandbox Code Playgroud)


Cyk*_*ker 10

如果您使用的是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))
Run Code Online (Sandbox Code Playgroud)

输出:

{"now": {"_isoformat": "2000-01-01T00:00:00-08:00"}}
True
Run Code Online (Sandbox Code Playgroud)

如果您使用的是python3.6或更低版本,并且您仅关心时间值(而不是时区),则可以使用datetime.timestamp()datetime.fromtimestamp()

如果您使用的是python3.6或更低版本,并且确实关心时区,则可以通过进行获取datetime.tzinfo,但是您必须自己序列化此字段;最简单的方法是_tzinfo在序列化对象中添加另一个字段;

最后,在所有这些示例中都要当心精度;


Sau*_*aha 9

json.dumps方法可以接受称为default的可选参数,该参数应为函数。每次JSON尝试转换值时,它都不知道如何转换将调用我们传递给它的函数。该函数将接收有问题的对象,并且应返回该对象的JSON表示形式。

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

print(json.dumps(d, default = myconverter)) 
Run Code Online (Sandbox Code Playgroud)


Sea*_*ond 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', ']']
Run Code Online (Sandbox Code Playgroud)

这使用复数作为示例,但您可以轻松创建一个类来编码日期(除了我认为JSON对日期有点模糊)


Mic*_*ner 8

我通常使用orjson. 不仅因为其出色的性能,还因为其出色的(符合 RFC-3339 )支持datetime

import orjson # via pip3 install orjson
from datetime import datetime

data = {"created_at": datetime(2022, 3, 1)}

orjson.dumps(data) # returns b'{"created_at":"2022-03-01T00:00:00"}'
Run Code Online (Sandbox Code Playgroud)

如果您想使用datetime.datetime没有 tzinfo 的对象作为 UTC,您可以添加相关选项:

orjson.dumps(data, option=orjson.OPT_NAIVE_UTC) # returns b'{"created_at":"2022-03-01T00:00:00+00:00"}'
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的答案。不知道为什么本机“json”模块无法处理日期时间,而其他语言中的几乎所有其他库都可以处理日期时间。这应该是标准。 (2认同)

zhi*_*ang 6

用一个例子试试这个来解析它:

#!/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()
Run Code Online (Sandbox Code Playgroud)


Ang*_*own 6

其实很简单。如果您需要经常序列化日期,请将它们作为字符串使用。如果需要,您可以轻松地将它们转换回日期时间对象。

如果您需要主要作为日期时间对象工作,那么在序列化之前将它们转换为字符串。

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'>
Run Code Online (Sandbox Code Playgroud)

如您所见,两种情况下的输出是相同的。只是类型不同。


Pet*_*ham 5

最简单的方法是将日期时间格式的dict部分更改为isoformat.该值实际上是isoformat中的一个字符串,json可以使用.

v_dict = version.dict()
v_dict['created_at'] = v_dict['created_at'].isoformat()
Run Code Online (Sandbox Code Playgroud)