"Datetime.datetime değil JSON serileştirilebilir" üstesinden nasıl gelinir?


742

Aşağıdaki gibi temel bir dikte var:

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

Yapmaya çalıştığımda jsonify(sample):

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

Sözlük örneğimin yukarıdaki hatayı aşabileceği şekilde ne yapabilirim?

Not: Alakalı olmasa da, sözlükler çıktılarımı mongodbyazdırdığım yerden çıktıların alınmasından üretilir .str(sample['somedate'])2012-08-08 21:46:24.862000


1
Bu genel olarak python mu, yoksa django mu?
jdi

1
Teknik olarak özellikle python, django kullanmıyorum, ancak mongodb'dan kayıtlar alıyorum.
Rolando


Mongoengine kullanıyorum, ancak pymongo'nun bu sorunu aşmanın veya üstesinden gelmenin daha iyi yolları varsa, lütfen söyleyin.
Rolando

3
Bağlantılı soru aslında datetime nesnesini serileştirmeye çalışmamanızı değil, serileştirmeden önce onu ortak ISO biçiminde bir dizeye dönüştürmeyi denemektir.
Thomas Kelley

Yanıtlar:


377

2018 için güncellendi

Orijinal yanıt, MongoDB "tarih" alanlarının şu şekilde temsil edilme biçimini içeriyordu:

{"$date": 1506816000000}

Json'a serileştirmek datetimeiçin genel bir Python çözümü istiyorsanız , bağımlılık gerektirmeyen hızlı bir çözüm için @jjmontes'in cevabına bakın.


Mongoengine (yorum başına) kullandığınız ve pymongo bir bağımlılık olduğundan, pymongo, json serileştirmesine yardımcı olacak yerleşik yardımcı programlara sahiptir:
http://api.mongodb.org/python/1.10.1/api/bson/json_util.html

Örnek kullanım (serileştirme):

from bson import json_util
import json

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

Örnek kullanım (serileştirme):

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

Django

Django, DjangoJSONEncoderbu tür düzgün bir şekilde ilgilenen yerel bir serileştirici sağlar .

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

Fark ettim DjangoJSONEncoderve defaultböyle bir özel kullanarak :

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
)

Bu Django biraz veri çıkarıyor mu?

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

Bu nedenle, bazı durumlarda buna dikkat etmeniz gerekebilir.


3
Birden fazla kütüphaneyi karıştırmak iyi / kötü bir uygulama mı, örneğin sorgu eklemek için mongoengine ve sorgu / geri alma için pymongo'ya sahip olmak mı?
Rolando

Bu kötü bir uygulama değil, sadece ana kütüphanenizin kullandığı kütüphanelere biraz bağımlılık anlamına gelir. Eğer mongoengine ihtiyacınız olan şeyi başaramazsanız, pymongo'ya düşersiniz. Onunla aynı Django MongoDB. Daha sonra, arka uç agnostik durumunu korumak için django ORM içinde kalmaya çalışacaksınız. Ancak bazen soyutlamada ihtiyacınız olanı yapamazsınız, böylece bir katmanı düşürürsünüz. Bu durumda, sadece JSON biçimine eşlik etmek için yardımcı program yöntemlerini kullandığınız için sorununuzla tamamen ilgisizdir.
jdi

Ben Flask ile bu çalışıyorum ve json.dump kullanarak, o uygulama / json döndürür etrafında bir jsonify () sarmalayıcı koyamıyorum görünüyor. Jsonify döndürme denemesi (json.dumps (örnek, varsayılan = json_util.default))
Rolando

2
@amit Bu sözdizimini ezberlemekle ilgili o kadar çok değil, çünkü belgeyi okumakta ve nereden ve ne zaman tekrar almam gerektiğini anlamak için kafamda yeterli bilgi depolamakla ilgili. Bu durumda, "Oh, json ile özel bir nesne" diyebilir ve sonra bu kullanımda hızla yenilenebilir
jdi

2
@guyskk Bunu 5 yıl önce yazdığımdan beri bjson veya mongo'daki değişiklikleri takip etmedim. Ancak, tarih saatinin serileştirilmesi üzerinde kontrol istiyorsanız, jgbarah tarafından verilen cevapta gösterildiği gibi kendi varsayılan işleyici işlevinizi yazmanız gerekir
jdi jdi

618

Tarihler ve her şeyi yiyor benim hızlı ve kirli JSON dökümü:

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

14
Bu harika, ama ne yazık ki ne olduğunu anlamadım? Herkes bu cevabı açıklayabilir mi?
Kishor Pawar

63
@KishorPawar: defaultserileştirilemeyen nesnelere uygulanan bir işlevdir. Bu durumda str, bilmediği her şeyi dizelere dönüştürür. Bu serileştirme için harika ama serileştirme sırasında çok büyük değil (dolayısıyla "hızlı ve kirli") gibi bir şey uyarı olmadan dize ified olabilir, örneğin bir işlev veya numpy dizi.
Mark

1
@Mark harika. Teşekkürler. Tarih gibi serileştirilemeyen değerlerin türünü bildiğinizde kullanışlıdır.
Kishor Pawar

2
Neden tüm hayatıma bunu bilmeden gittim. :)
Arel

1
@jjmontes, her şey için işe yaramaz, örneğin json.dumps({():1,type(None):2},default=str)yükseltir TypeError, tip veya demet olamaz.
alancalvitti

443

Diğer cevaplara dayanarak, sadece dizeleri dönüştüren datetime.datetimeve datetime.datenesnelere dönüştüren belirli bir serileştiriciye dayanan basit bir çözüm .

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

Nesne sınıfının olup olmadığını anlamak için, kodu yalnızca çek görüldüğü gibi datetime.datetimeya da datetime.date, ve daha sonra kullanır .isoformat()MM: kolayca JavaScript tarafından dekode edilmektedir SS (ISO 8601 biçiminde, GG-AA-GGTSS göre, bunun bir tefrika versiyonunu üretmek için ). Daha karmaşık serileştirilmiş temsiller istenirse, str () yerine başka bir kod kullanılabilir (örnekler için bu sorunun diğer yanıtlarına bakın). Kod, serileştirilemeyen türle çağrıldığı durumla başa çıkmak için bir istisna oluşturarak sona erer.

Bu json_serial işlevi aşağıdaki gibi kullanılabilir:

from datetime import datetime
from json import dumps

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

Json.dumps için varsayılan parametrenin nasıl çalıştığıyla ilgili ayrıntılar , json modülü belgelerinin Temel Kullanımı bölümünde bulunabilir .


5
evet doğru cevap, daha güzel ithalat datetime ve isinstance (obj, datetime.datetime), ben çok zaman kaybettim çünkü datetime ithalat datetime kullanmayın, yine de teşekkürler
Sérgio

12
ama bu nasıl doğru tip ile serileştirmeyi açıklamaz, değil mi?
BlueTrin

2
Hayır, @BlueTrin, bunun hakkında hiçbir şey söylemedi. Benim durumumda, kutunun dışında çalışan JavaScript'te serileştirmeyi kaldırıyorum.
jgbarah

1
Json modülü datetime nesnelerinin serileştirilmesini içerecek şekilde güncelleştirilirse bu beklenmedik davranışlara neden olur.
Justin

1
@serg Ancak zamanları UTC'ye dönüştürmek birleşir 01:00:00+01:00ve 02:00:00+00:00bağlama bağlı olarak aynı olması gerekmez. Elbette aynı noktayı ifade ederler, ancak ofset, değerin ilgili bir yönü olabilir.
Alfe

211

Ben sadece bu sorunla karşılaştım ve benim çözüm alt sınıf 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)

: Çağrınızda gibi bir şey yapmak I yukarıdaki cevapları birinden aldım.json.dumps(yourobj, cls=DateTimeEncoder).isoformat()


22
özel bir JSONEncoder uygulamak gitmek için uygun bir yol olmalıdır çünkü upped
3k-

25
Bu sadece en önemli cevap değil, aynı zamanda normal json kodlayıcının bir parçası olmalıdır. Sadece kod çözme daha az belirsiz olsaydı ..
Joost

4
Django kullananlar için bkz DjangoJSONEncoder. docs.djangoproject.com/tr/dev/topics/serialization/…
S. Kirby

4
Süper yararlı. Son satır olabilirreturn super(DateTimeEncoder, self).default(o)
Bob Stein

16
Python 3 ile son satır daha da basit:return super().default(o)
ariddell

124

Tarihi bir dizeye dönüştürme

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

10
Python'da bunu nasıl serileştirebilirim?
Ocak

62
Sorun, bir veri yapısına derinden gömülü birçok datetime nesneniz varsa veya rasgele. Bu güvenilir bir yöntem değil.
Rebs

3
seriyi kaldırmak oDate = datetime.datetime.strptime(sDate, '%Y-%m-%d %H:%M:%S.%f'). Alınan
Roman

13
Saat dilimi bilgilerini yoksayar. Bunu belirtmeden .now()yerel saati kullanan unutmayın . En azından .utcnow()kullanılmalıdır (ve sonra +0000 veya Z eklendi)
Daniel F

1
@DanielF At least .utcnow() should be usedTam datetime.now(timezone.utc)olarak önerilmez , şu uyarıya bakın: docs.python.org/3.8/library/… .
Toreno96

79

Bunun için pymongo kütüphanesine ihtiyaç duymayan veya kullanmak istemeyen diğerleri için bu küçük snippet ile datetime JSON dönüşümünü kolayca gerçekleştirebilirsiniz:

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

Sonra şöyle kullanın:

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

çıktı: 

'1365091796124'

1
millis=İf ifadesinin içine girintili olmamalı mı ? Daha yaygın olduğunu düşündüğüm ISO formatını elde etmek için str (obj) kullanmak da daha iyidir.
Rebs

Neden girintili olmasını istersiniz? Bu snippet çalışır ve elde edilen çıktı, javascript'ten kolayca seriden çıkartılabilir / ayrıştırılabilir.
Jay Taylor

5
Çünkü obj bir [saat, tarih, datetime] nesnesi olmayabilir
Rebs

2
yerel saat dilimi sıfır dışında bir UTC ofsetine sahipse örneğiniz yanlıştır (çoğunda). datetime.now()yerel saati döndürür (naif datetime nesnesi olarak), ancak kodunuz objsaat dilimine duyarlı değilse UTC'de olduğunu varsayar . datetime.utcnow()Bunun yerine kullanın .
jfs

1
Obcs dopy.python.org/2/library/json.html#basic-usage adresindeki Python dokümantasyon önerisine göre tanınmazsa, bir tür hata oluşturacak şekilde ayarlandı .
Jay Taylor

40

İşte benim çözümüm:

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

Sonra şöyle kullanabilirsiniz:

json.dumps(dictionnary, cls=DatetimeEncoder)

Katılıyorum. Çok daha iyi, en azından mongodb bağlamından. isinstance(obj, datetime.datetime)TypeError içinde yapabilir , işlemek için daha fazla tür ekleyebilir ve str(obj)veya ile bitirebilirsiniz repr(obj). Ve tüm çöplükleriniz sadece bu özel sınıfa işaret edebilir.
JL Peyret

@Natim bu çözüm en iyisidir. +1
Souvik Ray

20

Benzer bir soruna sahip bir başvurum var; yaklaşımım datetime değerini 6 maddelik bir liste (yıl, ay, gün, saat, dakika, saniye) olarak JSONize etmekti; 7 maddelik bir liste olarak mikrosaniye gidebilir, ama gerek yoktu:

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)

üretir:

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

Tasarruf edilen zaman datetime.utcnow ()
saurshaz

1
Datetime.utcnow () ile hangi hatayı görüyorsunuz? Benim için sorun değil.
kodlama

17

Benim çözümüm (daha az ayrıntı ile, sanırım):

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)

Sonra jsondumpsyerine kullanın json.dumps. Yazdırılacak:

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

İstiyorum, daha sonra defaultyöntemin basit bir bükümü ile buna başka özel durumlar ekleyebilirsiniz . Misal:

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
İsinstance kullanmalısınız (o, (datetime.date, datetime.datetime,)). Muhtemelen datetime.time da dahil zarar vermez.
Rebs

Bunun artık iyi bir çözüm olduğunu düşünmüyorum. Muhtemelen dönüşümler kodunuzda daha ayrıcalıklı bir yer ve ayrıca daha anlaşılır bir yer almalıdır. saydam işlev. Ama bilmiyorum.
fiatjaf

1
JSON, daha sonra işlenmek üzere verilerin serileştirilmesi için iyidir. Bu verilerin tam olarak ne olduğunu bilmiyor olabilirsiniz. Ve buna gerek yok. JSON'un seri hale getirilmesi sadece çalışmalıdır. Unicode'u ascii'ye dönüştürmek gibi. Python'un bunu belirsiz işlevler olmadan yapamaması, onu rahatsız edici hale getirir. Veritabanı doğrulaması ayrı bir sorun IMO'sudur.
Rebs

Hayır, "sadece işe yaramaz". Serileştirmenin nasıl gerçekleştiğini bilmiyorsanız ve daha sonra başka bir programdan / dilden verilere erişmek zorunda kalırsanız, kaybolursunuz.
fiatjaf

2
JSON genellikle dizeler, ints, kayan reklamlar, tarihler için kullanılır (diğerlerinin para birimi, sıcaklıklar da kullandığından eminim). Ancak datetime standart kütüphanenin bir parçasıdır ve seri / serileştirmeyi desteklemelidir. Bu soru olmasaydı, hala inanılmaz derecede karmaşık json lekelerimi (her zaman için yapıyı her zaman oluşturmadım) manuel olarak arayacağım ve 1'e 1'e
serileştirirdim

16

Bu Q zaman ve tekrar tekrar eder - json modülünü seri hale getirmenin datetime destekleyeceği şekilde düzeltmenin basit bir yolu.

import json
import datetime

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

Her zaman yaptığınız gibi json serileştirmeyi kullanın - bu kez datetime izoformat olarak serileştirilir.

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

Sonuç: '{"oluşturuldu": "2015-08-26T14: 21: 31.853855"}'

Daha fazla ayrıntıya ve bazı dikkat kelimelerine bakın: StackOverflow: Python ve JavaScript arasındaki JSON tarih


Maymun yaması FTW. Kötü şey, elbette bu, tüm uygulamanızdaki json modülünün davranışını değiştirir, bu da büyük bir uygulamada diğerlerini şaşırtabilir, bu nedenle genellikle bakım imho ile kullanılmalıdır.
Jaap Versteegh

15

Json.dumps yöntemi, varsayılan bir işlev olması beklenen isteğe bağlı bir parametre kabul edebilir. JSON bir değeri dönüştürmeye her çalıştığında, onu nasıl dönüştüreceğini bilmediğimiz değere çağıracağız. İşlev söz konusu nesneyi alır ve nesnenin JSON temsilini döndürmesi beklenir.

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

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

14

python3.7 kullanıyorsanız, en iyi çözüm datetime.isoformat()ve datetime.fromisoformat(); hem saf hem de farkında datetimenesnelerle çalışırlar :

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

çıktı:

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

python3.6 veya daha düşük bir sürümünü kullanıyorsanız ve yalnızca zaman değerini (zaman dilimini değil) önemsiyorsanız, datetime.timestamp()ve datetime.fromtimestamp()yerine kullanabilirsiniz ;

python3.6 veya daha düşük bir sürümünü kullanıyorsanız ve saat dilimi ile ilgileniyorsanız, bunu elde edebilirsiniz datetime.tzinfo, ancak bu alanı kendiniz serileştirmeniz gerekir; bunu yapmanın en kolay yolu _tzinfoserileştirilmiş nesneye başka bir alan eklemektir ;

son olarak, tüm bu örneklerde kesinliklere dikkat edin;


datetime.isoformat (), Python 2.7'de de mevcuttur: docs.python.org/2/library/…
powlo

11

Serileştirilebilir bir yöntem olarak yapmak için .strftime()yöntem üzerinde .datetime.now()yöntem kullanmalısınız .

İşte bir örnek:

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

Çıktı:

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

10

İşte "datetime değil JSON serileştirilebilir" sorunu üzerinden gelmek için basit bir çözümdür.

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)

Çıktı: -> {"tarih": "2015-12-16T04: 48: 20.024609"}


8

clsParametresiyle özel bir kodlayıcı sınıfı sağlamanız gerekir json.dumps. Dokümanlardan alıntı yapmak için :

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

Bu örnek olarak karmaşık sayılar kullanır, ancak tarihleri ​​kodlamak için kolayca bir sınıf oluşturabilirsiniz (JSON'un tarihler hakkında biraz bulanık olduğunu düşünüyorum)


5

Bunu yapmanın en basit yolu, datetime biçimindeki diktenin izoformat kısmını değiştirmek. Bu değer etkili bir şekilde, json ile tamamlanan isoformat içinde bir dize olacaktır.

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

5

Aslında oldukça basit. Tarihleri ​​sık sık serileştirmeniz gerekiyorsa, onlarla dize olarak çalışın. Gerekirse bunları datetime nesneleri olarak kolayca dönüştürebilirsiniz.

Çoğunlukla datetime nesneleri olarak çalışmanız gerekiyorsa, serileştirmeden önce bunları dize olarak dönüştürün.

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

Gördüğünüz gibi, çıktı her iki durumda da aynıdır. Sadece tür farklıdır.


3

Sonucu bir görünümde kullanıyorsanız, doğru bir yanıt verdiğinizden emin olun. API'ye göre jsonify aşağıdakileri yapar:

Bir application / json mimetype ile verilen bağımsız değişkenlerin JSON temsili ile bir Yanıt oluşturur.

Bu davranışı json.dumps ile taklit etmek için birkaç ekstra kod satırı eklemeniz gerekir.

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

Ayrıca jsonify'ın yanıtını tam olarak çoğaltmak için bir karar vermelisiniz. Yani, tüm dosya şöyle görünecek

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
Sorunun şişeyle ilgisi yok.
Zoran Pavlovic

2
Soru python ile ilgili. Cevabım soruyu python kullanarak çözüyor. OP, çözümün belirli kütüphaneleri içermesi veya hariç tutması gerekip gerekmediğini söylemedi. Bu soruyu okuyan ve alternatif isteyen herkes için de yararlıdır pymongo.
reubano

Soruları Flask ile değil Python ile ilgilidir . Flask, soruya cevabınızda bile gerekli değildir, bu yüzden kaldırmanızı öneririm.
Zoran Pavlovic

3

Ayrıştırmak için bunu bir örnekle deneyin:

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

Çözümüm ...

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    

Tamam, şimdi bazı testler.

# 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

İşte datetime'ı JSON'a ve geri dönüştürmek için tam çözümüm ..

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

Çıktı

{'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 Dosyası

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

Bu, dize, ints, float ve datetime nesneleri içe ve dışa aktarmamı sağladı. Diğer türler için genişletilmesi zor olmamalıdır.


1
Python 3 ile patlar TypeError: 'str' does not support the buffer interface. 'wb'Açık moddan dolayı olmalı 'w'. Aynı zamanda, tarihe benzer, '0000891618-05-000338'ancak eşleme düzeni olmayan verilere sahip olduğumuzda, serileştirmede de patlar .
omikron

2

dönüştürme date için string

date = str(datetime.datetime(somedatetimehere)) 

jjmontes cevap tam olarak bunu yapar, ancak her tarih için açıkça yapmak zorunda kalmadan ...
bluesummers

2

Genellikle, tarihler dizileştirmenin birkaç yolu vardır, örneğin:

  1. ISO dizesi kısa ve saat dilimi bilgilerini içerebilir, örn. @ Jgbarah'ın cevabı
  2. Zaman damgası (saat dilimi verileri kaybolur), örneğin @ JayTaylor'un yanıtı
  3. Özellikler sözlüğü (saat dilimi dahil).

Son yoldan iyiyseniz , json_tricks paketi zaman dilimleri dahil tarihleri, saatleri ve günleri işler.

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

hangi verir:

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

Yani tek yapman gereken

`pip install json_tricks`

ve json_tricksyerine json.

Kod çözme sırasında tek bir dize, int veya float olarak saklamamanın avantajı: sadece bir dize veya özellikle int veya float ile karşılaşırsanız, veri tarihi hakkında bir şey bilmeniz gerekir. Bir diksiyon olarak, meta verileri otomatik olarak deşifre edilebilecek şekilde depolayabilirsiniz.json_tricks sizin için yapar. Ayrıca insanlar için kolayca düzenlenebilir.

Feragatname: benim tarafımdan yapılmış. Çünkü aynı problemi yaşadım.


1

Ben sqlalchemy ile bir Sınıf içinde serialize dekoratör yazarken aynı hata mesajı var. Yani yerine:

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

Sadece jgbarah'ın isoformat () kullanma fikrini ödünç aldım ve orijinal değeri isoformat () ile ekledim, böylece şimdi şöyle görünüyor:

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

1

Kendi biçimlendirmenizi istiyorsanız hızlı bir düzeltme

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

İletişimin her iki tarafındaysanız, repson () ve eval () işlevlerini json ile birlikte kullanabilirsiniz.

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

Tarih saatini şu şekilde içe aktarmamalısınız

from datetime import datetime

eval şikayet edecek beri. Veya datetime değerini bir parametre olarak değerlendirebilirsiniz. Her durumda, bu işe yarayacak.


0

JSON olarak dökümü için django model nesnesini dışsallaştırırken aynı sorunla karşılaşmıştım. İşte nasıl çözeceğiniz.

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

Yukarıdaki fayda kullanımı:

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

0

Bu kütüphane superjson bunu yapabilir. Ve bu talimatı izleyerek kendi Python Nesneniz için kolayca özel json serileştirici https://superjson.readthedocs.io/index.html#extend .

Genel kavram:

kodunuzun python nesnesine göre doğru serileştirme / serileştirme yöntemini bulması gerekir. Genellikle, tam sınıf adı iyi bir tanımlayıcıdır.

Ve sonra ser / deser yönteminiz, nesnenizi genel bir Python türü, dikte, liste, dize, int, kayan nokta kombinasyonu olan normal bir Json serileştirilebilir nesneye dönüştürebilmelidir. Ve deser yöntemini tersine uygula.


-1

% 100 doğru olmayabilirim, ancak bu seri hale getirmenin basit yoludur

#!/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'> 
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.