Bagaimana cara mengatasi "datetime.datetime not JSON serializable"?

743

Saya memiliki perintah dasar sebagai berikut:

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

Ketika saya mencoba melakukannya jsonify(sample)saya mendapatkan:

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

Apa yang bisa saya lakukan sehingga sampel kamus saya dapat mengatasi kesalahan di atas?

Catatan: Meskipun mungkin tidak relevan, kamus dihasilkan dari pengambilan catatan dari mongodbtempat ketika saya mencetak str(sample['somedate']), hasilnya adalah 2012-08-08 21:46:24.862000.

Rolando
sumber
1
Apakah ini khusus python secara umum, atau mungkin Django?
jdi
1
Secara teknis khusus python, saya tidak menggunakan Django, tetapi mengambil catatan dari mongodb.
Rolando
kemungkinan duplikat daton JSON antara Python dan JavaScript
jdi
Saya menggunakan mongoengine, tetapi jika pymongo memiliki cara yang lebih baik untuk mengatasi ini atau mengatasinya, tolong beri tahu.
Rolando
3
Pertanyaan yang ditautkan pada dasarnya memberitahu Anda untuk tidak mencoba membuat serialisasi objek datetime, melainkan untuk mengubahnya menjadi string dalam format ISO umum sebelum membuat cerita bersambung.
Thomas Kelley

Jawaban:

377

Diperbarui untuk 2018

Jawaban asli mengakomodasi bagaimana bidang "date" MongoDB direpresentasikan sebagai:

{"$date": 1506816000000}

Jika Anda menginginkan solusi Python generik untuk serialisasi datetimeke json, lihat jawaban @jjmontes untuk solusi cepat yang tidak memerlukan dependensi.


Karena Anda menggunakan mongoengine (per komentar) dan pymongo adalah dependensi, pymongo memiliki utilitas bawaan untuk membantu dengan serialisasi json:
http://api.mongodb.org/python/1.10.1/api/bson/json_util.html

Contoh penggunaan (serialisasi):

from bson import json_util
import json

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

Contoh penggunaan (deserialization):

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

Django

Django menyediakan DjangoJSONEncoderserializer asli yang berhubungan dengan jenis ini dengan benar.

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

Satu perbedaan yang saya perhatikan antara DjangoJSONEncoderdan menggunakan custom defaultseperti ini:

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
)

Apakah itu Django menghapus sedikit data:

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

Jadi, Anda mungkin perlu berhati-hati tentang itu dalam beberapa kasus.

jdi
sumber
3
Apakah praktik yang baik / buruk untuk mencampurkan banyak pustaka yaitu memiliki mongoengine untuk menyisipkan dokumen dan pymongo untuk permintaan / pencarian?
Rolando
Ini bukan praktik yang buruk, itu hanya menyiratkan beberapa ketergantungan pada perpustakaan yang menggunakan perpustakaan utama Anda. Jika Anda tidak dapat memenuhi apa yang Anda butuhkan dari mongoengine, maka Anda drop down ke pymongo. Itu sama dengan Django MongoDB. Dengan yang kemudian, Anda akan mencoba untuk tetap berada dalam ORANG Django untuk mempertahankan status agnostik backend. Tetapi kadang-kadang Anda tidak dapat melakukan apa yang Anda butuhkan dalam abstraksi, jadi Anda menjatuhkan sebuah layer. Dalam hal ini, itu sama sekali tidak terkait dengan masalah Anda karena Anda hanya menggunakan metode utilitas untuk menyertai format JSON.
jdi
Saya mencoba ini dengan Flask dan tampaknya bahwa dengan menggunakan json.dump, saya tidak dapat menempatkan pembungkus jsonify () di sekitarnya sehingga kembali dalam aplikasi / json. Mencoba mengembalikan jsonify (json.dumps (contoh, default = json_util.default))
Rolando
2
@amit Ini bukan soal menghafal sintaksis, tetapi tentang cara membaca dokumentasi dengan baik dan menyimpan informasi yang cukup di kepala saya untuk mengenali di mana dan kapan saya harus mengambilnya kembali. Dalam hal ini, orang mungkin mengatakan "Oh objek khusus dengan json" dan kemudian dengan cepat menyegarkan penggunaan itu
jdi
2
@guyskk Saya belum melacak perubahan pada bjson atau mongo sejak saya menulis ini 5 tahun yang lalu. Tetapi jika Anda ingin mengontrol serialisasi datetime maka Anda perlu menulis fungsi handler default Anda sendiri seperti yang diilustrasikan dalam jawaban yang diberikan oleh jgbarah
jdi
619

Tempat pembuangan JSON cepat & kotor yang memakan tanggal dan segalanya:

json.dumps(my_dictionary, indent=4, sort_keys=True, default=str)
jjmontes
sumber
14
Ini luar biasa, tetapi sayangnya saya tidak mengerti apa yang terjadi? Adakah yang bisa menjelaskan jawaban ini?
Kishor Pawar
63
@KishorPawar: defaultadalah fungsi yang diterapkan pada objek yang tidak bisa serial. Dalam hal ini str, jadi itu hanya mengkonversi semua yang tidak diketahui menjadi string. Yang bagus untuk serialisasi tetapi tidak begitu bagus ketika deserializing (maka "cepat & kotor") karena segala sesuatu mungkin telah di-string-ified tanpa peringatan, misalnya fungsi atau array numpy.
Mark
1
@Ark mengagumkan. Terima kasih. Berguna ketika Anda tahu jenis nilai-nilai non-serializable seperti tanggal.
Kishor Pawar
2
Mengapa saya pergi sepanjang hidup saya tanpa mengetahui hal ini. :)
Arel
1
@ jjmontes, tidak berfungsi untuk semuanya, misalnya json.dumps({():1,type(None):2},default=str)kenaikan gaji TypeError, tidak bisa mengetik atau tuple.
alancalvitti
443

Membangun jawaban lain, solusi sederhana berdasarkan serializer spesifik yang baru saja mengkonversi datetime.datetimedan datetime.dateobjek ke string.

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

Seperti yang terlihat, kode hanya memeriksa untuk mengetahui apakah objek kelas datetime.datetimeatau datetime.date, dan kemudian menggunakan .isoformat()untuk menghasilkan versi serial itu, sesuai dengan format ISO 8601, YYYY-MM-DDTHH: MM: SS (yang mudah diterjemahkan oleh JavaScript ). Jika representasi serial yang lebih kompleks dicari, kode lain dapat digunakan sebagai ganti str () (lihat jawaban lain untuk pertanyaan ini sebagai contoh). Kode berakhir dengan memunculkan eksepsi, untuk menangani kasus ini disebut dengan tipe non-serializable.

Fungsi json_serial ini dapat digunakan sebagai berikut:

from datetime import datetime
from json import dumps

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

Rincian tentang cara kerja parameter default untuk json.dumps dapat ditemukan di Bagian Penggunaan Dasar dokumentasi modul json .

jgbarah
sumber
5
ya jawaban yang benar, impor datetime lebih cantik dan jika isinstance (obj, datetime.datetime), saya kehilangan banyak waktu karena tidak digunakan dari datetime impor datetime, toh terima kasih
Sérgio
12
tapi ini tidak menjelaskan cara deserialise dengan tipe yang benar, bukan?
BlueTrin
2
Tidak, @ BlueTrin, tidak ada yang mengatakan tentang itu. Dalam kasus saya, saya deserializing dalam JavaScript, yang bekerja di luar kotak.
jgbarah
1
Ini akan menyebabkan perilaku yang tidak terduga jika modul json pernah memperbarui untuk memasukkan serialisasi objek datetime.
Justin
1
@erg Tetapi mengubah waktu ke UTC akan menyatukan 01:00:00+01:00dan 02:00:00+00:00yang tidak seharusnya sama, tergantung pada konteksnya. Mereka merujuk ke titik yang sama dalam waktu tentu saja, tetapi offset mungkin merupakan aspek yang relevan dari nilai tersebut.
Alfe
211

Saya baru saja mengalami masalah ini dan solusi saya adalah dengan subkelas 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)

Dalam panggilan Anda melakukan sesuatu seperti: json.dumps(yourobj, cls=DateTimeEncoder)The .isoformat()saya dapatkan dari salah satu jawaban atas.

lenny
sumber
22
meningkat karena mengimplementasikan JSONEncoder kustom harus menjadi cara yang tepat untuk pergi
3k-
25
Tidak hanya seharusnya ini menjadi jawaban teratas, ini harus menjadi bagian dari json encoder biasa. Kalau saja decoding kurang ambigu ..
Joost
4
Bagi yang menggunakan Django, lihat DjangoJSONEncoder. docs.djangoproject.com/en/dev/topics/serialization/…
S. Kirby
4
Sangat membantu. Baris terakhir bisa jadireturn super(DateTimeEncoder, self).default(o)
Bob Stein
16
Dengan Python 3 baris terakhir bahkan lebih sederhana:return super().default(o)
ariddell
124

Ubah tanggal menjadi string

sample['somedate'] = str( datetime.utcnow() )
DA
sumber
10
Dan bagaimana saya bisa deserialize dengan Python?
wobmene
62
Masalahnya adalah jika Anda memiliki banyak objek datetime yang tertanam dalam dalam struktur data, atau mereka acak. Ini bukan metode yang dapat diandalkan.
Rebs
3
deserialize: oDate = datetime.datetime.strptime(sDate, '%Y-%m-%d %H:%M:%S.%f'). Format diperoleh dari: docs.python.org/2/library/datetime.html
Roman
13
Turun karena mengabaikan informasi zona waktu. Perlu diingat bahwa .now()menggunakan waktu setempat, tanpa menunjukkan ini. Setidaknya .utcnow()harus digunakan (dan kemudian +0000 atau Z ditambahkan)
Daniel F
1
@DanielF At least .utcnow() should be usedTidak persis, datetime.now(timezone.utc)direkomendasikan, lihat peringatan di: docs.python.org/3.8/library/… .
Toreno96
79

Untuk orang lain yang tidak perlu atau ingin menggunakan pymongo library untuk ini .. Anda dapat mencapai konversi JSON datetime dengan mudah dengan potongan kecil ini:

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

Kemudian gunakan seperti ini:

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

keluaran: 

'1365091796124'
Jay Taylor
sumber
1
Bukankah seharusnya millis=ada indentasi di dalam pernyataan if? Mungkin juga lebih baik menggunakan str (obj) untuk mendapatkan format ISO yang menurut saya lebih umum.
Rebs
Mengapa Anda ingin itu indentasi? Cuplikan ini berfungsi dan output yang dihasilkan dapat dengan mudah dideeralisasi / diurai dari javascript.
Jay Taylor
5
Karena obj mungkin bukan objek [waktu, tanggal, datetime]
Rebs
2
contoh Anda salah jika zona waktu lokal memiliki offset UTC non-nol (kebanyakan dari mereka). datetime.now()mengembalikan waktu lokal (sebagai objek datetime naif) tetapi kode Anda menganggap itu objdalam UTC jika tidak sadar zona waktu. Gunakan datetime.utcnow()sebagai gantinya.
jfs
1
Menyesuaikannya untuk meningkatkan kesalahan jenis jika obj tidak dikenali sesuai rekomendasi dokumentasi Python di docs.python.org/2/library/json.html#basic-usage .
Jay Taylor
40

Inilah solusi saya:

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

Maka Anda dapat menggunakannya seperti itu:

json.dumps(dictionnary, cls=DatetimeEncoder)
Natim
sumber
setuju. Jauh lebih baik, setidaknya di luar konteks mongodb. Anda dapat melakukannya isinstance(obj, datetime.datetime)di dalam TypeError, menambahkan lebih banyak jenis untuk ditangani, dan menyelesaikan dengan str(obj)atau repr(obj). Dan semua dump Anda hanya bisa menunjuk ke kelas khusus ini.
JL Peyret
@Natim solusi ini adalah yang terbaik. +1
Souvik Ray
20

Saya memiliki aplikasi dengan masalah serupa; pendekatan saya adalah JSONisasi nilai datetime sebagai daftar 6-item (tahun, bulan, hari, jam, menit, detik); Anda bisa pergi ke mikrodetik sebagai daftar 7-item, tapi saya tidak perlu:

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)

menghasilkan:

{'somedate': datetime.datetime(2013, 8, 1, 16, 22, 45, 890000), 'title': 'String'}
{"somedate": [2013, 8, 1, 16, 22, 45], "title": "String"}
codingatty
sumber
Gagal bekerja jika waktu yang disimpan disimpan dengan melakukan datetime.utcnow ()
saurshaz
1
Apa kesalahan yang Anda lihat dengan datetime.utcnow ()? Ini berfungsi baik untuk saya.
codingatty
17

Solusi saya (dengan lebih sedikit verbositas, saya pikir):

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)

Kemudian gunakan jsondumpssebagai ganti json.dumps. Itu akan mencetak:

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

Jika Anda ingin, nanti Anda dapat menambahkan kasing khusus lainnya dengan metode sederhana default. Contoh:

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)
fiatjaf
sumber
1
Anda harus menggunakan isinstance (o, (datetime.date, datetime.datetime,)). Mungkin tidak ada salahnya memasukkan datetime.time juga.
Rebs
Saya rasa ini bukan solusi yang baik lagi. Mungkin konversi harus mengambil tempat yang lebih istimewa - dan juga tempat yang lebih dimengerti - dalam kode Anda, sehingga Anda tahu apa yang Anda konversi ketika Anda memasukkan sesuatu ke dalam basis data, atau apa pun, alih-alih semuanya dilakukan oleh fungsi transparan. Tapi saya tidak tahu.
fiatjaf
1
JSON baik untuk serialisasi data untuk diproses nanti. Anda mungkin tidak tahu persis data apa itu. Dan Anda seharusnya tidak perlu melakukannya. Serialising JSON seharusnya bekerja. Sama seperti mengonversi unicode ke ascii. Ketidakmampuan Python untuk melakukan ini tanpa fungsi yang tidak jelas membuatnya mengganggu untuk digunakan. Validasi basis data adalah masalah terpisah IMO.
Rebs
Tidak, itu tidak seharusnya "hanya bekerja". Jika Anda tidak tahu bagaimana serialisasi terjadi dan harus mengakses data nanti dari program / bahasa lain, maka Anda hilang.
fiatjaf
2
JSON umumnya digunakan untuk string, int, float, tanggal (saya yakin orang lain menggunakan mata uang, suhu, biasanya juga). Tetapi datetime adalah bagian dari perpustakaan standar dan harus mendukung de / serialisasi. Jika bukan karena pertanyaan ini, saya masih akan secara manual mencari gumpalan json saya yang sangat kompleks (yang saya tidak selalu membuat struktur untuk) untuk tanggal dan serialisasi mereka 1 oleh 1.
Rebs
16

Q ini berulang-ulang - cara sederhana untuk menambal modul json sedemikian rupa sehingga serialisasi akan mendukung datetime.

import json
import datetime

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

Daripada menggunakan serialisasi json seperti biasa - kali ini dengan datetime diserialisasi sebagai isoformat.

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

Menghasilkan: '{"dibuat": "2015-08-26T14: 21: 31.853855"}'

Lihat lebih detail dan beberapa kata peringatan di: StackOverflow: JSON datetime antara Python dan JavaScript

davidhadas
sumber
Patch monyet FTW. Hal yang buruk tentu saja ini memodifikasi perilaku modul json di seluruh aplikasi Anda, yang mungkin mengejutkan orang lain dalam aplikasi besar, jadi umumnya harus digunakan dengan hati-hati.
Jaap Versteegh
15

Metode json.dumps dapat menerima parameter opsional yang disebut default yang diharapkan menjadi fungsi. Setiap kali JSON mencoba mengonversi suatu nilai, ia tidak tahu bagaimana mengonversinya akan memanggil fungsi yang kami berikan. Fungsi akan menerima objek yang dimaksud, dan diharapkan untuk mengembalikan representasi objek JSON.

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

print(json.dumps(d, default = myconverter)) 
Saurabh Saha
sumber
15

jika Anda menggunakan python3.7, maka solusi terbaik adalah menggunakan datetime.isoformat()dan datetime.fromisoformat(); mereka bekerja dengan objek yang naif dan sadar 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))

keluaran:

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

jika Anda menggunakan python3.6 atau lebih rendah, dan Anda hanya peduli dengan nilai waktu (bukan zona waktu), maka Anda dapat menggunakan datetime.timestamp()dan datetime.fromtimestamp()sebagai gantinya;

jika Anda menggunakan python3.6 atau di bawah, dan Anda memang peduli dengan zona waktu, maka Anda bisa mendapatkannya melalui datetime.tzinfo, tetapi Anda harus membuat serialisasi bidang ini sendiri; cara termudah untuk melakukan ini adalah menambahkan bidang lain _tzinfodalam objek bersambung;

Akhirnya, berhati-hatilah dengan semua contoh dalam contoh ini;

Cyker
sumber
datetime.isoformat () juga hadir dalam Python 2.7: docs.python.org/2/library/…
powlo
11

Anda harus menggunakan .strftime()metode pada .datetime.now()metode untuk menjadikannya sebagai metode serializable .

Ini sebuah contoh:

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

Keluaran:

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

Berikut adalah solusi sederhana untuk mengatasi masalah "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)

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

ob92
sumber
8

Anda harus menyediakan kelas pembuat enkode khusus dengan clsparameter json.dumps. Mengutip dari dokumen :

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

Ini menggunakan bilangan kompleks sebagai contoh, tetapi Anda dapat dengan mudah membuat kelas untuk menyandikan tanggal (kecuali saya pikir JSON sedikit kabur tentang tanggal)

Sean Redmond
sumber
5

Cara paling sederhana untuk melakukan ini adalah mengubah bagian dict yang ada dalam format datetime menjadi isoformat. Nilai itu akan secara efektif menjadi string di isoformat yang json baik-baik saja dengan.

v_dict = version.dict()
v_dict['created_at'] = v_dict['created_at'].isoformat()
Peter Graham
sumber
5

Sebenarnya itu cukup sederhana. Jika Anda harus sering membuat serial tanggal, maka buatlah itu sebagai string. Anda dapat dengan mudah mengubahnya kembali sebagai objek datetime jika diperlukan.

Jika Anda perlu bekerja sebagian besar sebagai objek datetime, lalu konversikan sebagai string sebelum membuat cerita bersambung.

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

Seperti yang Anda lihat, hasilnya sama dalam kedua kasus. Hanya jenisnya yang berbeda.

AngelDown
sumber
3

Jika Anda menggunakan hasil dalam tampilan pastikan untuk mengembalikan respons yang tepat. Menurut API, jsonify melakukan hal berikut:

Menciptakan Respons dengan representasi JSON dari argumen yang diberikan dengan mimetype aplikasi / json.

Untuk meniru perilaku ini dengan json.dumps Anda harus menambahkan beberapa baris kode tambahan.

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

Anda juga harus mengembalikan dict untuk sepenuhnya mereplikasi respons jsonify. Jadi, seluruh file akan terlihat seperti ini

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
Ruben
sumber
1
Pertanyaan tidak ada hubungannya dengan labu.
Zoran Pavlovic
2
Pertanyaannya adalah tentang python. Jawaban saya memecahkan pertanyaan menggunakan python. OP tidak mengatakan apakah solusi harus memasukkan atau mengecualikan perpustakaan tertentu. Ini juga berguna bagi siapa pun yang membaca pertanyaan ini yang menginginkan alternatif pymongo.
reubano
Mereka bertanya tentang Python dan bukan tentang Flask. Flask bahkan tidak diperlukan dalam jawaban Anda untuk pertanyaan, jadi saya sarankan Anda menghapusnya.
Zoran Pavlovic
3

Coba yang ini dengan contoh untuk menguraikannya:

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

Solusi saya ...

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, sekarang beberapa tes.

# 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
macm
sumber
2

Berikut ini adalah solusi lengkap saya untuk mengubah datetime ke JSON dan kembali ..

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

Keluaran

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

File JSON

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

Ini memungkinkan saya untuk mengimpor dan mengekspor objek string, int, float dan datetime. Seharusnya tidak sulit untuk memperluas untuk jenis lain.

Hovo
sumber
1
Itu meledak di Python 3 dengan TypeError: 'str' does not support the buffer interface. Itu karena 'wb'mode terbuka, seharusnya 'w'. Itu juga berhembus deserialization ketika kita memiliki data yang mirip dengan tanggal seperti '0000891618-05-000338'tetapi tidak cocok dengan pola.
omikron
2

Konversikan date ke string

date = str(datetime.datetime(somedatetimehere)) 
Rana Nematollahi
sumber
jjmontes menjawab melakukan hal itu, tetapi tanpa keharusan untuk melakukannya secara eksplisit untuk setiap tanggal ...
bluesummers
2

Secara umum ada beberapa cara untuk membuat serial berita, seperti:

  1. String ISO, pendek dan dapat menyertakan info zona waktu, misalnya jawaban @ jgbarah
  2. Stempel waktu (data zona waktu hilang), misalnya jawaban @ JayTaylor
  3. Kamus properti (termasuk zona waktu).

Jika Anda setuju dengan cara terakhir, paket json_tricks menangani tanggal, waktu, dan waktu termasuk zona waktu.

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

pemberian yang mana:

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

Jadi yang perlu Anda lakukan adalah

`pip install json_tricks`

dan kemudian impor dari json_tricksbukan json.

Keuntungan dari tidak menyimpannya sebagai string tunggal, int atau float muncul ketika decoding: jika Anda menemukan hanya string atau terutama int atau float, Anda perlu mengetahui sesuatu tentang data untuk mengetahui apakah itu merupakan waktu. Sebagai dikt, Anda dapat menyimpan metadata sehingga dapat diterjemahkan secara otomatis, yang json_tricksmemang cocok untuk Anda. Ini juga mudah diedit untuk manusia.

Penafian: itu dibuat oleh saya. Karena saya punya masalah yang sama.

Menandai
sumber
1

Saya mendapat pesan kesalahan yang sama saat menulis dekorator bersambung di dalam Kelas dengan sqlalchemy. Jadi alih-alih:

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

Saya hanya meminjam ide jgbarah tentang menggunakan isoformat () dan menambahkan nilai asli dengan isoformat (), sehingga sekarang terlihat seperti:

                  ...
                 'date_birth':self.date_birth.isoformat(),
                  ...
Zhang Treefish
sumber
1

Perbaikan cepat jika Anda ingin memformat sendiri

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)
Arash
sumber
1

Jika Anda berada di kedua sisi komunikasi Anda dapat menggunakan fungsi repr () dan eval () bersama dengan 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))

Anda tidak boleh mengimpor datetime sebagai

from datetime import datetime

karena eval akan mengeluh. Atau Anda bisa melewatkan datetime sebagai parameter untuk eval. Bagaimanapun ini harus berhasil.

ThunderBear
sumber
0

Saya pernah mengalami masalah yang sama ketika mengeksternalisasi objek model Django untuk dibuang sebagai JSON. Inilah cara Anda bisa menyelesaikannya.

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
naren
sumber
0
def j_serial(o):     # self contained
    from datetime import datetime, date
    return str(o).split('.')[0] if isinstance(o, (datetime, date)) else None

Penggunaan utilitas di atas:

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

Superjson perpustakaan ini dapat melakukannya. Dan Anda dapat dengan mudah mengubahsuaikan serializer json untuk Objek Python Anda sendiri dengan mengikuti instruksi ini https://superjson.readthedocs.io/index.html#extend .

Konsep umum adalah:

kode Anda perlu menemukan metode serialisasi / deserialisasi yang tepat berdasarkan objek python. Biasanya, nama kelas lengkap adalah pengidentifikasi yang baik.

Dan kemudian metode ser / deser Anda harus dapat mengubah objek Anda menjadi objek serialson Json biasa, kombinasi tipe python generik, dict, list, string, int, float. Dan terapkan metode deser Anda secara terbalik.

MacSanhe
sumber
-1

Saya mungkin tidak 100% benar tetapi, ini adalah cara sederhana untuk membuat cerita bersambung

#!/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'> 
Siwa
sumber