Python 和 JavaScript 之间的 JSON 日期时间

IT技术 javascript python json
2021-01-15 01:08:19

我想使用JSON从 Python 以序列化形式发送 datetime.datetime 对象,使用JSON在 JavaScript 中反序列化。做这个的最好方式是什么?

6个回答

您可以将 'default' 参数添加到 json.dumps 来处理此问题:

date_handler = lambda obj: (
    obj.isoformat()
    if isinstance(obj, (datetime.datetime, datetime.date))
    else None
)
json.dumps(datetime.datetime.now(), default=date_handler)
'"2010-04-20T20:08:21.634121"'

这是ISO 8601格式。

更全面的默认处理函数:

def handler(obj):
    if hasattr(obj, 'isoformat'):
        return obj.isoformat()
    elif isinstance(obj, ...):
        return ...
    else:
        raise TypeError, 'Object of type %s with value of %s is not JSON serializable' % (type(obj), repr(obj))

更新:添加了类型和值的输出。
更新:也处理日期

json.dumps 也不知道如何转换它们,但是异常被抑制了。遗憾的是,单行 lambda 修复有其缺点。如果您希望在未知数上引发异常(这是个好主意),请使用我在上面添加的函数。
2021-03-19 01:08:19
可以调整 lambda 以调用非日期时间类型的基本实现,因此可以在需要时引发 TypeError: dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime) else json.JSONEncoder().default(obj)
2021-03-20 01:08:19
完整的输出格式也应该有时区......而 isoformat() 不提供这个功能......所以你应该确保在返回之前将该信息附加到字符串上
2021-03-22 01:08:19
问题是,如果您在 list/dict 中有一些其他对象,此代码会将它们转换为 None。
2021-03-24 01:08:19
这是最好的方法。为什么没有选择这个作为答案?
2021-04-07 01:08:19

对于跨语言项目,我发现包含RfC 3339日期的字符串是最好的方法。RFC 3339 日期如下所示:

  1985-04-12T23:20:50.52Z

我认为大部分格式是显而易见的。唯一有点不寻常的可能是末尾的“Z”。它代表 GMT/UTC。您还可以为 CEST(夏季为德国)添加时区偏移量,例如 +02:00。我个人更喜欢将所有内容保留在 UTC 中,直到显示为止。

为了显示、比较和存储,您可以将其保留为所有语言的字符串格式。如果您需要日期进行计算,可以轻松地将其转换回大多数语言中的本机日期对象。

所以像这样生成JSON:

  json.dump(datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ'))

不幸的是,Javascript 的 Date 构造函数不接受 RfC 3339 字符串,但Internet 上有许多解析器可用。

huTools.hujson尝试处理您在 Python 代码中可能遇到的最常见的编码问题,包括日期/日期时间对象,同时正确处理时区。

superjoe30:请参阅stackoverflow.com/questions/455580/ ... 了解如何做到这一点
2021-03-14 01:08:19
json.dumps(datetime.datetime.now().isoformat()) 是魔法发生的地方。
2021-03-23 01:08:19
simplejson 的美妙之处在于,如果我有一个复杂的数据结构,它会解析它并将其转换为 JSON。如果我必须为每个日期时间对象执行 json.dumps(datetime.datetime.now().isoformat()) ,我就会失去它。有没有办法解决这个问题?
2021-03-27 01:08:19
@jrk - 我没有得到从datetime对象到isoformat字符串的自动转换对我来说,simplejson.dumps(datetime.now())收益TypeError: datetime.datetime(...) is not JSON serializable
2021-04-02 01:08:19
这种日期格式化机制是由datetime: datetime.isoformat() 和 by 原生支持的,默认情况下simplejson它将datetime对象转储isoformat字符串。无需手动strftime黑客攻击。
2021-04-07 01:08:19

我已经解决了。

假设您有一个使用 datetime.now() 创建的 Python 日期时间对象d它的value是:

datetime.datetime(2011, 5, 25, 13, 34, 5, 787000)

您可以将其序列化为 JSON 作为 ISO 8601 日期时间字符串:

import json    
json.dumps(d.isoformat())

示例日期时间对象将被序列化为:

'"2011-05-25T13:34:05.787000"'

这个值,一旦在 Javascript 层接收到,就可以构造一个 Date 对象:

var d = new Date("2011-05-25T13:34:05.787000");

从 Javascript 1.8.5 开始,Date 对象有一个 toJSON 方法,它以标准格式返回一个字符串。因此,要将上述 Javascript 对象序列化回 JSON,命令将是:

d.toJSON()

这会给你:

'2011-05-25T20:34:05.787Z'

这个字符串一旦在 Python 中收到,就可以反序列化回日期时间对象:

datetime.strptime('2011-05-25T20:34:05.787Z', '%Y-%m-%dT%H:%M:%S.%fZ')

这会产生以下 datetime 对象,它与您开始使用的对象相同,因此是正确的:

datetime.datetime(2011, 5, 25, 20, 34, 5, 787000)
时区会把这个搞砸。让我们假设您在 python 中使用 UTC 工作(否则只有疯子才会这样做)-python 的 JSON 输出没有时区,因此 JavaScript 会将其解释为本地时区。JavaScript d.toJSON 将再次转换为 UTC。因此,对于英国浏览器上的示例日期(2011-04-25)(夏令时,UTC+1)python 输出 13:34 - JS 将其解释为本地时区或 UTC 12:34 - JS 然后输出 UTC 所以12:34。Python 会将其解释为 12:34。你已经浪费了一个小时。(或者一整天,如果你只处理日期而不是时间)。除了冬天。
2021-04-04 01:08:19

使用json,您可以继承 JSONEncoder 并覆盖 default() 方法以提供您自己的自定义序列化程序:

import json
import datetime

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

然后,你可以这样称呼它:

>>> DateTimeJSONEncoder().encode([datetime.datetime.now()])
'["2010-06-15T14:42:28"]'
次要增强 - 使用obj.isoformat()您还可以使用更常见的dumps()调用,它采用其他有用的参数(如indent): simplejson.dumps(myobj, cls=JSONEncoder, ...)
2021-04-09 01:08:19
因为那会调用 JSONEncoder 的父方法,而不是 DateTimeJSONEncoder 的父方法。IE,你会上升两个级别。
2021-04-10 01:08:19

这是使用标准库jsonmodule递归编码和解码 datetime.datetime 和 datetime.date 对象的相当完整的解决方案这需要 Python >= 2.6 因为%fdatetime.datetime.strptime() 格式字符串中格式代码从那时起才被支持。对于 Python 2.5 支持,%f在尝试转换 ISO 日期字符串之前删除并从 ISO 日期字符串中去除微秒,但当然你会失去微秒精度。为了与来自其他来源的 ISO 日期字符串(可能包括时区名称或 UTC 偏移量)进行互操作,您可能还需要在转换之前去除日期字符串的某些部分。有关 ISO 日期字符串(和许多其他日期格式)的完整解析器,请参阅第三方dateutilmodule。

仅当 ISO 日期字符串是 JavaScript 文字对象表示法中的值或对象内的嵌套结构中的值时,解码才有效。作为顶级数组的项目的 ISO 日期字符串将不会被解码。

即这有效:

date = datetime.datetime.now()
>>> json = dumps(dict(foo='bar', innerdict=dict(date=date)))
>>> json
'{"innerdict": {"date": "2010-07-15T13:16:38.365579"}, "foo": "bar"}'
>>> loads(json)
{u'innerdict': {u'date': datetime.datetime(2010, 7, 15, 13, 16, 38, 365579)},
u'foo': u'bar'}

这也是:

>>> json = dumps(['foo', 'bar', dict(date=date)])
>>> json
'["foo", "bar", {"date": "2010-07-15T13:16:38.365579"}]'
>>> loads(json)
[u'foo', u'bar', {u'date': datetime.datetime(2010, 7, 15, 13, 16, 38, 365579)}]

但这并没有按预期工作:

>>> json = dumps(['foo', 'bar', date])
>>> json
'["foo", "bar", "2010-07-15T13:16:38.365579"]'
>>> loads(json)
[u'foo', u'bar', u'2010-07-15T13:16:38.365579']

这是代码:

__all__ = ['dumps', 'loads']

import datetime

try:
    import json
except ImportError:
    import simplejson as json

class JSONDateTimeEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, (datetime.date, datetime.datetime)):
            return obj.isoformat()
        else:
            return json.JSONEncoder.default(self, obj)

def datetime_decoder(d):
    if isinstance(d, list):
        pairs = enumerate(d)
    elif isinstance(d, dict):
        pairs = d.items()
    result = []
    for k,v in pairs:
        if isinstance(v, basestring):
            try:
                # The %f format code is only supported in Python >= 2.6.
                # For Python <= 2.5 strip off microseconds
                # v = datetime.datetime.strptime(v.rsplit('.', 1)[0],
                #     '%Y-%m-%dT%H:%M:%S')
                v = datetime.datetime.strptime(v, '%Y-%m-%dT%H:%M:%S.%f')
            except ValueError:
                try:
                    v = datetime.datetime.strptime(v, '%Y-%m-%d').date()
                except ValueError:
                    pass
        elif isinstance(v, (dict, list)):
            v = datetime_decoder(v)
        result.append((k, v))
    if isinstance(d, list):
        return [x[1] for x in result]
    elif isinstance(d, dict):
        return dict(result)

def dumps(obj):
    return json.dumps(obj, cls=JSONDateTimeEncoder)

def loads(obj):
    return json.loads(obj, object_hook=datetime_decoder)

if __name__ == '__main__':
    mytimestamp = datetime.datetime.utcnow()
    mydate = datetime.date.today()
    data = dict(
        foo = 42,
        bar = [mytimestamp, mydate],
        date = mydate,
        timestamp = mytimestamp,
        struct = dict(
            date2 = mydate,
            timestamp2 = mytimestamp
        )
    )

    print repr(data)
    jsonstring = dumps(data)
    print jsonstring
    print repr(loads(jsonstring))
如果您打印日期datetime.datetime.utcnow().isoformat()[:-3]+"Z",它将与 JSON.stringify() 在 javascript 中产生的完全一样
2021-04-01 01:08:19