如何克服“ datetime.datetime无法JSON序列化”?


739

我有一个基本的命令,如下所示:

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 2012年

1
从技术上讲,它专门是python,我不是在使用django,而是从mongodb中检索记录。
罗兰多2012年


我正在使用mongoengine,但如果pymongo具有解决或克服此问题的更好方法,请告知。
罗兰多2012年

3
链接的问题实际上是在告诉您不要尝试序列化datetime对象,而是在序列化之前将其转换为通用ISO格式的字符串。
Thomas Kelley

Answers:


377

更新于2018

原始答案适应了MongoDB“日期”字段表示为:

{"$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)

用法示例(反序列化):

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

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
)

我注意到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进行查询/检索)是好/坏做法吗?
罗兰多2012年

这不是一个坏习惯,它只意味着对您的主库使用的库有一定的依赖性。如果您无法从mongoengine上完成所需的工作,那么您就可以使用pymongo。与相同Django MongoDB。对于后者,您将尝试保留在Django ORM中以维持后端不可知状态。但是有时您不能做抽象中需要做的事情,因此您需要放下一层。在这种情况下,它完全与您的问题无关,因为您只是使用实用程序方法来伴随JSON格式。
jdi 2012年

我正在使用Flask进行尝试,并且似乎通过使用json.dump,我无法在其周围放置jsonify()包装器,使其返回application / json。尝试返回jsonify(json.dumps(sample,default = json_util.default))
Rolando

2
@amit与其说是记住语法,不如说是因为它擅长阅读文档并在脑海中存储足够的信息以识别何时和何时需要再次检索它。在这种情况下,您可能会说“哦,一个带有json的自定义对象”,然后快速刷新该用法
jdi 2015年

2
@guyskk自从我5年前写这篇文章以来,我还没有跟踪bjson或mongo中的更改。但是,如果您想控制日期时间的序列化,则需要编写自己的默认处理函数,如jgbarah
jdi

615

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

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

13
这太棒了,但是不幸的是我不明白发生了什么事?谁能解释这个答案?
基什·帕瓦

61
@KishorPawar:default是应用于无法序列化的对象的函数。在这种情况下,它是str,因此它将只是将不知道的所有内容转换为字符串。这对于序列化非常有用,但是在反序列化(因此“快速又肮脏”)时就不那么理想了,因为任何东西都可以在没有警告的情况下进行字符串化,例如函数或numpy数组。
2016年

1
@马克很棒。谢谢。当您知道那些不可序列化的值的类型(例如日期)时,此选项很有用。
Kishor Pawar

2
为什么我一生都不知道呢。:)
Arel

1
@jjmontes不能适用于所有情况,例如json.dumps({():1,type(None):2},default=str)raises 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
是的正确答案,更漂亮的进口日期时间,如果isinstance(OBJ,datetime.datetime),我失去了很多时间,因为不从日期时间日期时间进口用,反正感谢
塞尔吉奥

12
但这不能解释如何使用正确的类型反序列化它,不是吗?
BlueTrin 2015年

2
不,@BlueTrin,对此没有任何评论。就我而言,我正在使用JavaScript进行反序列化,它可以直接使用。
jgbarah

1
如果json模块曾经更新为包括datetime对象的序列化,则这将导致意外行为。
贾斯汀

1
@serg但是将时间转换为UTC将会统一01:00:00+01:0002:00:00+00:00并且根据上下文,这不应该是相同的。它们当然指的是同一时间点,但是偏移量可能是该值的一个相关方面。
Alfe

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).isoformat()我从上面的答案之一了。


22
更新,因为实现自定义JSONEncoder应该是正确的方法
3k-

25
这不仅是最好的答案,而且应该是常规json编码器的一部分。如果只有解码是那么模糊..
Joost的

4
对于使用Django的用户,请参见DjangoJSONEncoderdocs.djangoproject.com/en/dev/topics/serialization/…–
S. Kirby,

4
超级有帮助。最后一行可能是return super(DateTimeEncoder, self).default(o)
Bob Stein

16
使用Python 3,最后一行更加简单:return super().default(o)
ariddell

123

将日期转换为字符串

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

10
我如何在Python中反序列化它?
wobmene 2014年

62
问题是如果您有许多嵌入数据结构中的日期时间对象,或者它们是随机的。这不是一种可靠的方法。
Rebs

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

对于不需要或想要使用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格式可能也更好。
Rebs 2014年

您为什么要缩进它?此代码段有效,并且可以很容易地从javascript中反序列化/分析所得的输出。
杰伊·泰勒

5
因为obj可能不是[时间,日期,日期时间]对象
Rebs

2
如果本地时区的UTC偏移量不为零(大多数情况下),则您的示例不正确。datetime.now()返回本地时间(作为朴素的datetime对象),但是您的代码假定obj它不支持时区,则假定它位于UTC中。使用datetime.utcnow()代替。
jfs 2014年

1
如果根据docs.python.org/2/library/json.html#basic-usage上的Python文档建议,无法识别obj,则对其进行了调整,以引发类型错误。
杰伊·泰勒

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
Souvik Ray

20

我有一个类似问题的应用程序;我的方法是将datetime值作为6项列表(年,月,日,时,分,秒)进行JSON化;您可以将微秒作为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 2013年

1
您在datetime.utcnow()中看到什么错误?对我来说还可以。
2013年

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也可能不会受到损害。
Rebs

我认为这不再是一个好的解决方案。转换可能会在代码中占据更特权的位置,并且也应该更易于理解,因此您知道将东西放入数据库时​​的转换是什么,而不是由数据库来完成一切。透明功能。但是我不知道。
fiatjaf 2014年

1
JSON非常适合序列化数据以供以后处理。您可能不确切知道该数据是什么。而且您不需要。序列化JSON应该可以正常工作。就像将unicode转换为ASCII应该一样。Python无法做到这一点而又没有晦涩难懂的功能,这使其使用起来很烦人。数据库验证是IMO的另一个问题。
Rebs

不,它不应该“正常工作”。如果您不知道序列化是如何发生的,并且以后不得不从另一个程序/语言访问数据,那么您会迷路。
fiatjaf 2014年

2
JSON通常用于字符串,整数,浮点数,日期(我敢肯定其他人也经常使用货币,温度)。但是datetime是标准库的一部分,应该支持反序列化。如果不是这个问题,我仍然会手动搜索难以置信的复杂json blob(我并不总是为其创建结构)以获取日期并将其序列
化为1。– Rebs

16

这个Q一次又一次地重复-修补json模块的一种简单方法,以便序列化支持datetime。

import json
import datetime

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

比起您一如既往地使用json序列化-这次将datetime序列化为isoformat。

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

结果:'{“ created”:“ 2015-08-26T14:21:31.853855”}“

有关更多详细信息,请注意: StackOverflow:Python和JavaScript之间的JSON日期时间


猴子补丁FTW。令人讨厌的是,这当然会修改整个应用程序中json模块的行为,这可能会使大型应用程序中的其他人感到惊讶,因此通常应谨慎使用imho。
Jaap Versteegh

15

json.dumps方法可以接受称为default的可选参数,该参数应为函数。每次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()method on .datetime.now()method使其成为可序列化的方法。

这是一个例子:

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

最简单的方法是将日期时间格式的字典部分更改为isoformat。该值将有效地是json可以使用的isoformat字符串。

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表示的响应,该响应具有application / 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
问题与烧瓶无关。
Zoran Pavlovic 2014年

2
问题是关于python。我的答案使用python解决了问题。OP没有说明解决方案是否应包含或排除某些库。对于阅读此问题的其他人想要替代的人也很有用pymongo
reubano

他们的问题是双方关于Python和约烧瓶中。在回答问题时甚至不需要烧瓶,因此建议您将其删除。
Zoran Pavlovic 2014年

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

通常,有几种方法可以序列化日期时间,例如:

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

您不应该将datetime导入为

from datetime import datetime

因为eval会抱怨。或者,您可以将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可以做到。您可以按照以下说明轻松为自己的Python对象自定义json序列化程序:https://superjson.readthedocs.io/index.html#extend

一般概念是:

您的代码需要根据python对象找到正确的序列化/反序列化方法。通常,完整的类名是一个很好的标识符。

然后,您的ser / deser方法应该能够将您的对象转换为常规的Json可序列化对象,该对象是通用python类型,dict,list,string,int,float的组合。并反向实施您的deser方法。


-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'> 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.