Làm cách nào để vượt qua datetime.datetime không JSON serializable?


742

Tôi có một lệnh cơ bản như sau:

sample = {}
sample['title'] = "String"
sample['somedate'] = somedatetimehere

Khi tôi cố gắng làm, jsonify(sample)tôi nhận được:

TypeError: datetime.datetime(2012, 8, 8, 21, 46, 24, 862000) is not JSON serializable

Tôi có thể làm gì để mẫu từ điển của tôi có thể khắc phục lỗi ở trên?

Lưu ý: Mặc dù có thể không liên quan, nhưng từ điển được tạo ra từ việc truy xuất các bản ghi ra khỏi mongodbnơi tôi in ra str(sample['somedate']), đầu ra là 2012-08-08 21:46:24.862000.


1
Đây có phải là con trăn nói chung, hoặc có thể là django?
JDI

1
Về mặt kỹ thuật nó đặc biệt là python, tôi không sử dụng django, nhưng lấy các bản ghi ra khỏi mongodb.
Rolando

có thể trùng lặp của JSON datetime giữa Python và JavaScript
JDI

Tôi đang sử dụng mongoengine, nhưng nếu pymongo có cách tốt hơn để khắc phục điều này hoặc khắc phục điều này, xin vui lòng cho biết.
Rolando

3
Câu hỏi được liên kết về cơ bản là bảo bạn không cố gắng tuần tự hóa đối tượng datetime, mà là chuyển đổi nó thành một chuỗi theo định dạng ISO phổ biến trước khi tuần tự hóa.
Thomas Kelley

Câu trả lời:


377

Cập nhật năm 2018

Câu trả lời ban đầu phù hợp với cách các trường "ngày" MongoDB được trình bày là:

{"$date": 1506816000000}

Nếu bạn muốn một giải pháp Python chung để tuần tự hóa datetimethành json, hãy xem câu trả lời của @jjmontes để biết giải pháp nhanh chóng không yêu cầu phụ thuộc.


Vì bạn đang sử dụng mongoengine (mỗi bình luận) và pymongo là một phụ thuộc, pymongo có các tiện ích tích hợp để trợ giúp cho việc xê-ri hóa json:
http://api.mongodb.org/python/1.10.1/api/bson/json_util.html

Ví dụ sử dụng (tuần tự hóa):

from bson import json_util
import json

json.dumps(anObject, default=json_util.default)

Ví dụ sử dụng (khử lưu huỳnh):

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

Django

Django cung cấp một DjangoJSONEncoderserializer bản địa liên quan đến loại này đúng.

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

Một điểm khác biệt tôi nhận thấy giữa DjangoJSONEncodervà sử dụng một tùy chỉnh defaultnhư thế này:

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
)

Có phải Django tước một chút dữ liệu:

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

Vì vậy, bạn có thể cần phải cẩn thận về điều đó trong một số trường hợp.


3
Có phải thực tế tốt / xấu khi trộn nhiều thư viện tức là có mongoengine để chèn tài liệu và pymongo cho truy vấn / truy xuất không?
Rolando

Thực tế không tệ, nó chỉ ngụ ý một số phụ thuộc vào các thư viện mà thư viện chính của bạn sử dụng. Nếu bạn không thể hoàn thành những gì bạn cần từ mongoengine, thì bạn thả xuống pymongo. Nó giống với Django MongoDB. Với phần sau, bạn sẽ cố gắng ở trong django ORM để duy trì trạng thái bất khả tri phụ trợ. Nhưng đôi khi bạn không thể làm những gì bạn cần trong sự trừu tượng, vì vậy bạn thả xuống một lớp. Trong trường hợp này, nó hoàn toàn không liên quan đến vấn đề của bạn vì bạn chỉ đang sử dụng các phương thức tiện ích để đi kèm với định dạng JSON.
JDI

Tôi đang thử điều này với Flask và có vẻ như bằng cách sử dụng json.dump, tôi không thể đặt một trình bao bọc jsonify () xung quanh nó để nó trả về trong ứng dụng / json. Cố gắng thực hiện trả về jsonify (json.dumps (mẫu, mặc định = json_util.default))
Rolando

2
@amit Nó không quá nhiều về việc ghi nhớ cú pháp, vì nó giúp đọc tốt tài liệu và lưu trữ đủ thông tin trong đầu để nhận ra tôi cần lấy lại ở đâu và khi nào. Trong trường hợp này, người ta có thể nói "Ôi một đối tượng tùy chỉnh với json" và sau đó nhanh chóng làm mới cách sử dụng đó
jdi

2
@guyskk Tôi chưa theo dõi những thay đổi trong bjson hoặc mongo kể từ khi tôi viết bài này 5 năm trước. Nhưng nếu bạn muốn kiểm soát việc tuần tự hóa datetime thì bạn cần phải viết hàm xử lý mặc định của riêng bạn như được minh họa trong câu trả lời được đưa ra bởi jgbarah
jdi

619

Kết xuất JSON nhanh và bẩn của tôi ăn ngày và mọi thứ:

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

14
Điều này thật tuyệt vời, nhưng tiếc là tôi không hiểu chuyện gì đã xảy ra? Bất cứ ai có thể giải thích câu trả lời này?
Kishor Pawar

63
@KishorPawar: defaultlà một hàm được áp dụng cho các đối tượng không tuần tự hóa. Trong trường hợp này str, nó chỉ chuyển đổi mọi thứ mà nó không biết thành chuỗi. Điều này là tuyệt vời cho việc tuần tự hóa nhưng không tuyệt vời khi giải tuần tự hóa (do đó "nhanh & bẩn") vì bất cứ điều gì có thể đã được xâu chuỗi mà không có cảnh báo, ví dụ như một hàm hoặc mảng numpy.
Đánh dấu

1
@Mark tuyệt vời. Cảm ơn. Hữu ích khi bạn biết loại của các giá trị không tuần tự hóa như ngày.
Kishor Pawar

2
Tại sao tôi đi cả đời mà không biết điều này. :)
Arel

1
@jjmontes, không hoạt động cho tất cả mọi thứ, ví dụ: json.dumps({():1,type(None):2},default=str)tăng TypeError, không thể có loại hoặc tuple.
alancalvitti

443

Dựa trên các câu trả lời khác, một giải pháp đơn giản dựa trên bộ nối tiếp cụ thể chỉ chuyển đổi datetime.datetimedatetime.datecác đối tượng thành chuỗi.

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

Như đã thấy, mã chỉ kiểm tra xem liệu đối tượng có thuộc lớp datetime.datetimehay không datetime.date, và sau đó sử dụng .isoformat()để tạo ra một phiên bản nối tiếp của nó, theo định dạng ISO 8601, YYYY-MM-DDTHH: MM: SS (dễ dàng được giải mã bằng JavaScript ). Nếu các biểu diễn tuần tự phức tạp hơn được tìm kiếm, mã khác có thể được sử dụng thay vì str () (xem các câu trả lời khác cho câu hỏi này để biết ví dụ). Mã kết thúc bằng cách đưa ra một ngoại lệ, để xử lý trường hợp nó được gọi với kiểu không tuần tự hóa.

Hàm json_serial này có thể được sử dụng như sau:

from datetime import datetime
from json import dumps

print dumps(datetime.now(), default=json_serial)

Các chi tiết về cách tham số mặc định cho json.dumps hoạt động có thể được tìm thấy trong Phần Sử dụng cơ bản của tài liệu mô-đun json .


5
vâng, câu trả lời đúng, thời gian nhập dữ liệu đẹp hơn và nếu isinstance (obj, datetime.datetime), tôi đã mất nhiều thời gian vì không sử dụng từ datetime nhập datetime, dù sao cũng cảm ơn
Sérgio

12
Nhưng điều này không giải thích làm thế nào để giải trừ nó với đúng loại, phải không?
BlueTrin

2
Không, @BlueTrin, không có gì nói về điều đó. Trong trường hợp của tôi, tôi đang khử lưu huỳnh trong JavaScript, hoạt động vượt trội.
jgbarah

1
Điều này sẽ gây ra hành vi không mong muốn nếu mô-đun json từng cập nhật để bao gồm tuần tự hóa các đối tượng datetime.
Justin

1
@serg Nhưng thời gian chuyển đổi sang UTC sẽ thống nhất 01:00:00+01:0002:00:00+00:00không được cho là giống nhau, tùy thuộc vào ngữ cảnh. Tất nhiên, chúng đề cập đến cùng một thời điểm, nhưng phần bù có thể là một khía cạnh liên quan của giá trị.
Alfe

211

Tôi vừa gặp phải vấn đề này và giải pháp của tôi là phân lớp 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)

Trong cuộc gọi của bạn làm như sau: json.dumps(yourobj, cls=DateTimeEncoder)Các .isoformat()tôi nhận được từ một trong những câu trả lời ở trên.


22
bị đảo lộn bởi vì triển khai một bộ mã hóa tùy chỉnh nên là cách thích hợp để đi
3k-

25
Đây không chỉ là câu trả lời hàng đầu, đây phải là một phần của bộ mã hóa json thông thường. Nếu chỉ giải mã thì ít mơ hồ hơn ..
Joost

4
Đối với những người sử dụng Django, xem DjangoJSONEncoder. docs.djangoproject.com/en/dev/topics/serialization/ khăn
S. Kirby

4
Siêu hữu ích. Dòng cuối cùng có thể làreturn super(DateTimeEncoder, self).default(o)
Bob Stein

16
Với Python 3, dòng cuối cùng thậm chí còn đơn giản hơn:return super().default(o)
ariddell

124

Chuyển đổi ngày thành một chuỗi

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

10
Và làm thế nào tôi có thể giải tuần tự hóa nó trong Python?
wobmene

62
Vấn đề là nếu bạn có nhiều đối tượng datetime được nhúng sâu trong cấu trúc dữ liệu hoặc chúng là ngẫu nhiên. Đây không phải là một phương pháp đáng tin cậy.
Hoàn trả

3
để giải trừ : oDate = datetime.datetime.strptime(sDate, '%Y-%m-%d %H:%M:%S.%f'). Các định dạng thu được từ: docs.python.org/2/l Library / datetime.html
La Mã

13
Downvote vì nó bỏ qua thông tin múi giờ. Hãy nhớ rằng .now()sử dụng thời gian địa phương, mà không chỉ ra điều này. Ít nhất .utcnow()nên được sử dụng (và sau đó là +0000 hoặc Z được nối thêm)
Daniel F

1
@DanielF At least .utcnow() should be usedKhông chính xác, datetime.now(timezone.utc)được khuyến khích, thấy cảnh báo trong: docs.python.org/3.8/library/... .
Toreno96

79

Đối với những người khác không cần hoặc không muốn sử dụng thư viện pymongo cho việc này .. bạn có thể dễ dàng đạt được chuyển đổi JSON theo thời gian với đoạn mã nhỏ này:

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

Sau đó sử dụng nó như vậy:

import datetime, json
print json.dumps(datetime.datetime.now(), default=default)

đầu ra: 

'1365091796124'

1
Không nên millis=thụt lề trong câu lệnh if? Có lẽ tốt hơn là sử dụng str (obj) để có được định dạng ISO mà tôi nghĩ là phổ biến hơn.
Hoàn trả

Tại sao bạn muốn nó được thụt lề? Đoạn mã này hoạt động và kết quả đầu ra có thể dễ dàng được giải nén / phân tích cú pháp từ javascript.
Jay Taylor

5
Bởi vì obj có thể không phải là một đối tượng [thời gian, ngày, thời gian]
Hoàn trả

2
ví dụ của bạn không chính xác nếu múi giờ địa phương có độ lệch UTC khác không (hầu hết trong số chúng). datetime.now()trả về giờ địa phương (dưới dạng đối tượng datetime ngây thơ) nhưng mã của bạn giả sử đó objlà trong UTC nếu nó không nhận biết múi giờ. Sử dụng datetime.utcnow()thay thế.
jfs

1
Điều chỉnh nó để tăng lỗi loại nếu obj không được nhận dạng theo khuyến nghị tài liệu Python tại docs.python.org/2/l Library / json.html # basic-usage .
Jay Taylor

40

Đây là giải pháp của tôi:

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

Sau đó, bạn có thể sử dụng nó như thế:

json.dumps(dictionnary, cls=DatetimeEncoder)

đồng ý. Tốt hơn nhiều, ít nhất là ra khỏi bối cảnh mongodb. Bạn có thể thực hiện isinstance(obj, datetime.datetime)trong TypeError, thêm nhiều loại để xử lý và kết thúc bằng str(obj)hoặc repr(obj). Và tất cả các bãi của bạn chỉ có thể trỏ đến lớp chuyên ngành này.
JL Peyret

@Natim giải pháp này là tốt nhất. +1
Souvik Ray

20

Tôi có một ứng dụng với một vấn đề tương tự; Cách tiếp cận của tôi là JSON hóa giá trị datetime dưới dạng danh sách 6 mục (năm, tháng, ngày, giờ, phút, giây); bạn có thể truy cập micrô giây dưới dạng danh sách 7 mục, nhưng tôi không cần phải:

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)

sản xuất:

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

Không thể hoạt động nếu thời gian được lưu được lưu bằng cách thực hiện datetime.utcnow ()
saurshaz

1
Bạn đang gặp lỗi gì với datetime.utcnow ()? Nó hoạt động tốt cho tôi.
mã hóa

17

Giải pháp của tôi (với độ dài ít hơn, tôi nghĩ vậy):

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)

Sau đó sử dụng jsondumpsthay thế json.dumps. Nó sẽ in:

>>> jsondumps({'today': datetime.date.today()})
'{"today": "2013-07-30"}'

Tôi muốn, sau này bạn có thể thêm các trường hợp đặc biệt khác vào đây với một defaultphương pháp đơn giản . Thí dụ:

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
Bạn nên sử dụng isinstance (o, (datetime.date, datetime.datetime,)). Có lẽ sẽ không bao gồm cả datetime.time nữa.
Hoàn trả

Tôi không nghĩ rằng đây là một giải pháp tốt nữa. Có lẽ các chuyển đổi sẽ chiếm một vị trí đặc quyền hơn - và cũng là một nơi dễ hiểu hơn - trong mã của bạn, để bạn biết bạn đang chuyển đổi thành gì khi đưa mọi thứ vào cơ sở dữ liệu, hoặc bất cứ điều gì, thay vì mọi thứ được thực hiện bởi chức năng minh bạch. Nhưng tôi không biết.
fiatjaf

1
JSON tốt cho việc tuần tự hóa dữ liệu để xử lý sau này. Bạn có thể không biết chính xác dữ liệu đó là gì. Và bạn không cần phải làm vậy. Tuần tự hóa JSON chỉ nên hoạt động. Cũng giống như chuyển đổi unicode thành ascii nên. Python không có khả năng làm điều này mà không có các chức năng tối nghĩa làm cho nó khó chịu khi sử dụng. Xác thực cơ sở dữ liệu là một vấn đề riêng biệt IMO.
Hoàn trả

Không, nó không nên "chỉ hoạt động". Nếu bạn không biết làm thế nào việc xê-ri hóa xảy ra và phải truy cập dữ liệu sau đó từ một chương trình / ngôn ngữ khác, thì bạn đã bị mất.
fiatjaf

2
JSON thường được sử dụng cho chuỗi, ints, float, date (Tôi chắc chắn những người khác sử dụng tiền tệ, nhiệt độ, thường là quá). Nhưng datetime là một phần của thư viện chuẩn và nên hỗ trợ khử / tuần tự hóa. Nếu không có câu hỏi này, tôi vẫn sẽ tự tìm kiếm các đốm màu cực kỳ phức tạp của mình (mà tôi không phải lúc nào cũng tạo cấu trúc cho) cho các ngày và nối tiếp chúng 1
Trả lời

16

Q này lặp đi lặp lại nhiều lần - một cách đơn giản để vá mô-đun json sao cho việc tuần tự hóa sẽ hỗ trợ datetime.

import json
import datetime

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

Hơn sử dụng tuần tự hóa json như bạn vẫn thường làm - lần này với datetime được tuần tự hóa dưới dạng isoformat.

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

Kết quả là: '{"đã tạo": "2015-08-26T14: 21: 31,853855"}'

Xem thêm chi tiết và một số từ thận trọng tại: StackOverflow: JSON datetime giữa Python và JavaScript


Khỉ vá FTW. Tất nhiên, điều khó chịu là điều này sửa đổi hành vi của mô-đun json trong toàn bộ ứng dụng của bạn, điều này có thể gây ngạc nhiên cho những người khác trong một ứng dụng lớn, do đó thường nên được sử dụng với imho.
Jaap Versteegh

15

Phương thức json.dumps có thể chấp nhận một tham số tùy chọn được gọi là mặc định được dự kiến ​​là một hàm. Mỗi khi JSON cố gắng chuyển đổi một giá trị, nó không biết cách chuyển đổi nó sẽ gọi hàm mà chúng ta đã truyền cho nó. Hàm này sẽ nhận được đối tượng trong câu hỏi và dự kiến ​​sẽ trả về biểu diễn JSON của đối tượng.

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

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

14

nếu bạn đang sử dụng python3.7, thì giải pháp tốt nhất là sử dụng datetime.isoformat()datetime.fromisoformat(); họ làm việc với cả những datetimeđối tượng ngây thơ và nhận thức :

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

đầu ra:

{"now": {"_isoformat": "2000-01-01T00:00:00-08:00"}}
True

nếu bạn đang sử dụng python3.6 trở xuống và bạn chỉ quan tâm đến giá trị thời gian (không phải múi giờ), thì bạn có thể sử dụng datetime.timestamp()datetime.fromtimestamp()thay vào đó;

nếu bạn đang sử dụng python3.6 trở xuống và bạn quan tâm đến múi giờ, thì bạn có thể truy cập nó datetime.tzinfo, nhưng bạn phải tự tuần tự hóa trường này; cách dễ nhất để làm điều này là thêm một trường khác _tzinfotrong đối tượng được tuần tự hóa;

cuối cùng, hãy cẩn thận với các biện pháp trong tất cả các ví dụ này;


datetime.isoformat () cũng có mặt trong Python 2.7: docs.python.org/2/library/...
powlo

11

Bạn nên sử dụng .strftime()phương thức trên .datetime.now()phương thức để làm cho nó như là một phương thức tuần tự hóa .

Đây là một ví dụ:

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

Đầu ra:

Out[0]: {'a': 1, 'b': 2, 'time': '2017-10-31T15:16:30'}

10

Đây là một giải pháp đơn giản để vượt qua vấn đề "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)

Đầu ra: -> {"ngày": "2015-12-16T04: 48: 20.024609"}


8

Bạn phải cung cấp một lớp mã hóa tùy chỉnh với clstham số là json.dumps. Để trích dẫn từ các tài liệu :

>>> 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', ']']

Điều này sử dụng các số phức làm ví dụ, nhưng bạn có thể dễ dàng tạo một lớp để mã hóa ngày (ngoại trừ tôi nghĩ JSON hơi mờ về ngày)


5

Cách đơn giản nhất để làm điều này là thay đổi một phần của dict có định dạng datetime thành isoformat. Giá trị đó thực sự sẽ là một chuỗi trong isoformat mà json vẫn ổn.

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

5

Thật ra nó khá đơn giản. Nếu bạn cần thường xuyên tuần tự hóa ngày, sau đó làm việc với chúng dưới dạng chuỗi. Bạn có thể dễ dàng chuyển đổi chúng trở lại dưới dạng đối tượng datetime nếu cần.

Nếu bạn cần làm việc chủ yếu dưới dạng đối tượng datetime, sau đó chuyển đổi chúng dưới dạng chuỗi trước khi tuần tự hóa.

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

Như bạn có thể thấy, đầu ra là như nhau trong cả hai trường hợp. Chỉ có loại là khác nhau.


3

Nếu bạn đang sử dụng kết quả trong chế độ xem, hãy chắc chắn trả về phản hồi thích hợp. Theo API, jsonify thực hiện như sau:

Tạo một Phản hồi với biểu diễn JSON của các đối số đã cho bằng một ứng dụng / json mimetype.

Để bắt chước hành vi này với json.dumps, bạn phải thêm một vài dòng mã.

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

Bạn cũng nên trả lại một lệnh để sao chép đầy đủ phản hồi của jsonify. Vì vậy, toàn bộ tập tin sẽ trông như thế này

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
Câu hỏi không có gì để làm với bình.
Zoran Pavlovic

2
Câu hỏi là về con trăn. Câu trả lời của tôi giải quyết câu hỏi bằng cách sử dụng python. OP không cho biết liệu giải pháp nên bao gồm hay loại trừ một số thư viện nhất định. Nó cũng hữu ích cho bất cứ ai khác đọc câu hỏi này, người muốn có một giải pháp thay thế pymongo.
reubano

Họ đặt câu hỏi là về Python và không phải về Flask. Flask thậm chí không cần thiết trong câu trả lời của bạn cho câu hỏi, vì vậy tôi khuyên bạn nên loại bỏ nó.
Zoran Pavlovic

3

Hãy thử một ví dụ này để phân tích nó:

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

Giải pháp của tôi ...

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    

Ok, bây giờ một số thử nghiệm.

# 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

Đây là giải pháp đầy đủ của tôi để chuyển đổi datetime thành JSON và trở lại ..

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

Đầu ra

{'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}

Tệp JSON

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

Điều này đã cho phép tôi nhập và xuất các chuỗi, ints, float và các đối tượng datetime. Không nên khó mở rộng cho các loại khác.


1
Nó phát nổ trong Python 3 với TypeError: 'str' does not support the buffer interface. Đó là vì 'wb'chế độ mở, nên 'w'. Nó cũng thổi vào quá trình khử lưu huỳnh khi chúng ta có dữ liệu tương tự như ngày '0000891618-05-000338'nhưng không khớp mẫu.
omikron

2

Chuyển đổi date thành string

date = str(datetime.datetime(somedatetimehere)) 

jjmontes trả lời chính xác điều đó, nhưng không cần thiết phải làm điều đó một cách rõ ràng cho mỗi ngày ...
bluesummers

2

Nói chung, có một số cách để tuần tự hóa datetimes, như:

  1. Chuỗi ISO, ngắn và có thể bao gồm thông tin múi giờ, ví dụ: câu trả lời của @ jgbarah
  2. Dấu thời gian (dữ liệu múi giờ bị mất), ví dụ: câu trả lời của @ JayTaylor
  3. Từ điển các thuộc tính (bao gồm cả múi giờ).

Nếu bạn ổn với cách cuối cùng, gói json_tricks xử lý ngày, thời gian và thời gian biểu bao gồm cả múi giờ.

from datetime import datetime
from json_tricks import dumps
foo = {'title': 'String', 'datetime': datetime(2012, 8, 8, 21, 46, 24, 862000)}
dumps(foo)

cung cấp cho:

{"title": "String", "datetime": {"__datetime__": null, "year": 2012, "month": 8, "day": 8, "hour": 21, "minute": 46, "second": 24, "microsecond": 862000}}

Vì vậy, tất cả những gì bạn cần làm là

`pip install json_tricks`

và sau đó nhập từ json_tricksthay vì json.

Ưu điểm của việc không lưu trữ nó dưới dạng một chuỗi, int hoặc float xuất hiện khi giải mã: nếu bạn gặp phải một chuỗi hoặc đặc biệt là int hoặc float, bạn cần biết một số thứ về dữ liệu để biết đó có phải là datetime không. Là một dict, bạn có thể lưu trữ siêu dữ liệu để nó có thể được giải mã tự động, đó là những gì json_trickslàm cho bạn. Nó cũng dễ dàng chỉnh sửa cho con người.

Disclaimer: nó được thực hiện bởi tôi. Bởi vì tôi đã có cùng một vấn đề.


1

Tôi đã nhận được thông báo lỗi tương tự trong khi viết trình trang trí tuần tự hóa trong Class với sqlalchemy. Vì vậy, thay vì:

Class Puppy(Base):
    ...
    @property
    def serialize(self):
        return { 'id':self.id,
                 'date_birth':self.date_birth,
                  ...
                }

Tôi chỉ đơn giản mượn ý tưởng của jgbarah về việc sử dụng isoformat () và nối thêm giá trị ban đầu với isoformat (), để bây giờ nó trông giống như:

                  ...
                 'date_birth':self.date_birth.isoformat(),
                  ...

1

Một sửa chữa nhanh nếu bạn muốn định dạng của riêng bạn

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

Nếu bạn ở cả hai phía của giao tiếp, bạn có thể sử dụng các hàm repr ()eval () cùng với 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))

Bạn không nên nhập datetime như

from datetime import datetime

vì eval sẽ phàn nàn. Hoặc bạn có thể truyền datetime như một tham số cho eval. Trong mọi trường hợp điều này nên làm việc.


0

Tôi đã gặp vấn đề tương tự khi xuất hiện đối tượng mô hình django để kết xuất dưới dạng JSON. Đây là cách bạn có thể giải quyết nó.

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

Sử dụng các tiện ích trên:

import datetime
serial_d = j_serial(datetime.datetime.now())
if serial_d:
    print(serial_d)  # output: 2018-02-28 02:23:15

0

Superjson thư viện này có thể làm điều đó. Và bạn có thể dễ dàng tùy chỉnh trình tuần tự json cho Đối tượng Python của riêng mình bằng cách làm theo hướng dẫn này https://superjson.readthedocs.io/index.html#extend .

Khái niệm chung là:

mã của bạn cần xác định đúng phương thức tuần tự hóa / giải tuần tự hóa dựa trên đối tượng python. Thông thường, tên lớp đầy đủ là một định danh tốt.

Và sau đó phương thức ser / deser của bạn sẽ có thể chuyển đổi đối tượng của bạn thành một đối tượng tuần tự hóa Json thông thường, một sự kết hợp của kiểu python chung, dict, list, string, int, float. Và thực hiện phương pháp deser của bạn đảo ngược.


-1

Tôi có thể không đúng 100% nhưng, đây là cách đơn giản để thực hiện tuần tự hóa

#!/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'> 
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.