วิธีการเอาชนะ“ datetime.datetime ไม่ใช่ JSON ต่อเนื่องได้”?


741

ฉันมี dict พื้นฐานดังนี้

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

เมื่อฉันพยายามjsonify(sample)ฉันจะได้รับ:

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

ฉันจะทำอะไรได้บ้างเพื่อให้ตัวอย่างพจนานุกรมของฉันสามารถเอาชนะข้อผิดพลาดได้

หมายเหตุ:แม้ว่ามันอาจจะไม่เกี่ยวข้องพจนานุกรมถูกสร้างขึ้นจากการเรียกดูบันทึกออกจากmongodbที่เมื่อฉันพิมพ์ออกผลลัพธ์คือstr(sample['somedate'])2012-08-08 21:46:24.862000


1
นี่เป็นงูใหญ่โดยเฉพาะหรือเป็น django หรือเปล่า?
jdi

1
ในทางเทคนิคแล้วมันเป็น python โดยเฉพาะฉันไม่ได้ใช้ django แต่เป็นการดึงข้อมูลออกมาจาก mongodb
Rolando

เป็นไปได้ที่ซ้ำกันของวันที่และเวลาของJSON ระหว่าง Python และ JavaScript
jdi

ฉันกำลังใช้ mongoengine แต่ถ้า pymongo มีวิธีที่ดีกว่าในการแก้ไขปัญหานี้หรือเอาชนะมันได้โปรดบอก
Rolando

3
คำถามที่ถูกเชื่อมโยงนั้นบอกคุณว่าอย่าพยายามซีเรียลไลซ์วัตถุ datetime แต่ให้แปลงเป็นสตริงในรูปแบบ ISO ทั่วไปก่อนทำการจัดลำดับ
โทมัสตวัดที่

คำตอบ:


377

อัปเดตในปี 2561

คำตอบเดิมที่ใช้ในการแสดงฟิลด์ MongoDB "วันที่" เป็น:

{"$date": 1506816000000}

หากคุณต้องการโซลูชัน Python ทั่วไปสำหรับการทำให้เป็นอนุกรมdatetimeเป็น json โปรดดูที่คำตอบ @jjmontesเพื่อหาวิธีแก้ปัญหาอย่างรวดเร็วซึ่งไม่จำเป็นต้องพึ่งพา


ในขณะที่คุณใช้ mongoengine (ต่อความคิดเห็น) และ pymongo เป็นสิ่งที่ต้องพึ่งพา pymongo มีเครื่องมือในตัวเพื่อช่วยในการจัดลำดับ json:
http://api.mongodb.org/python/1.10.1/api/bson/json_util.html

ตัวอย่างการใช้งาน (การทำให้เป็นอนุกรม):

from bson import json_util
import json

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

ตัวอย่างการใช้งาน (ดีซีเรียลไลเซชัน):

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

Django

Django ให้DjangoJSONEncoderserializer พื้นเมืองที่เกี่ยวข้องกับประเภทนี้อย่างถูกต้อง

ดูhttps://docs.djangoproject.com/en/dev/topics/serialization/#djangojsonencoder

from django.core.serializers.json import DjangoJSONEncoder

return json.dumps(
  item,
  sort_keys=True,
  indent=1,
  cls=DjangoJSONEncoder
)

ข้อแตกต่างอย่างหนึ่งที่ฉันสังเกตเห็นระหว่างการDjangoJSONEncoderใช้งานกำหนดเองdefaultเช่นนี้

import datetime
import json

def default(o):
    if isinstance(o, (datetime.date, datetime.datetime)):
        return o.isoformat()

return json.dumps(
  item,
  sort_keys=True,
  indent=1,
  default=default
)

นั่นคือ Django ดึงข้อมูลเล็กน้อย:

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

ดังนั้นคุณอาจต้องระมัดระวังในบางกรณี


3
เป็นการดีหรือไม่ดีที่จะผสมหลาย ๆ ไลบรารีเช่นมี mongoengine สำหรับใส่เอกสารและ pymongo เพื่อค้นหา / ดึงข้อมูล?
Rolando

มันไม่ได้เป็นการปฏิบัติที่ไม่ดีมันก็หมายถึงการพึ่งพาห้องสมุดที่ห้องสมุดหลักของคุณใช้ หากคุณไม่สามารถบรรลุสิ่งที่คุณต้องการจาก mongoengine คุณก็สามารถเลื่อนลงไปที่ pymongo ได้ Django MongoDBมันเหมือนกันกับ ในภายหลังคุณจะพยายามอยู่ภายใน django ORM เพื่อรักษาสถานะผู้ไม่เชื่อเรื่องพระเจ้าไว้เบื้องหลัง แต่บางครั้งคุณไม่สามารถทำสิ่งที่คุณต้องการในสิ่งที่เป็นนามธรรมได้ดังนั้นคุณจึงเลื่อนเลเยอร์ลง ในกรณีนี้มันไม่เกี่ยวข้องกับปัญหาของคุณอย่างสมบูรณ์เนื่องจากคุณเพียงแค่ใช้วิธีการยูทิลิตี้เพื่อมาพร้อมกับรูปแบบ JSON
jdi

ฉันลองใช้ Flask แล้วและดูเหมือนว่าโดยใช้ json.dump ฉันไม่สามารถใส่เสื้อคลุม jsonify () ล้อมรอบมันได้ ความพยายามในการส่งคืน jsonify (json.dumps (ตัวอย่าง, ค่าเริ่มต้น = json_util.default))
Rolando

2
@amit มันไม่ได้เกี่ยวกับการจดจำไวยากรณ์มากนักเพราะมันเป็นการดีในการอ่านเอกสารและการเก็บข้อมูลที่เพียงพอในหัวของฉันเพื่อให้รู้ว่าที่ไหนและเมื่อไหร่ที่ฉันต้องดึงมันอีกครั้ง ในกรณีนี้หนึ่งอาจพูดว่า "โอ้วัตถุที่กำหนดเองกับ json" แล้วรีเฟรชอย่างรวดเร็วในการใช้งานนั้น
jdi

2
@guyskk ฉันไม่ได้ติดตามการเปลี่ยนแปลงใน bjson หรือ mongo ตั้งแต่ฉันเขียนสิ่งนี้เมื่อ 5 ปีที่แล้ว แต่ถ้าคุณต้องการควบคุมการซีเรียลไลเซชันของ
jdi

617

การถ่ายโอนข้อมูล JSON ที่รวดเร็วและสกปรกของฉันที่กินเดทและทุกอย่าง:

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

14
มันเยี่ยมมาก แต่น่าเสียดายที่ฉันไม่เข้าใจว่าเกิดอะไรขึ้น ใครช่วยอธิบายคำตอบนี้ได้บ้าง
Kishor Pawar

62
@KishorPawar: defaultเป็นฟังก์ชั่นที่ใช้กับวัตถุที่ไม่ได้ต่อเนื่องกัน ในกรณีนี้มันstrก็แค่แปลงทุกอย่างที่มันไม่รู้จักกับสตริง ซึ่งดีสำหรับซีเรียลไลซ์เซชั่น แต่ไม่ค่อยดีนักเมื่อ deserializing (ดังนั้น "ด่วน & สกปรก") เป็นอะไรก็ได้ที่อาจเป็น string-ified โดยไม่มีการเตือนเช่นฟังก์ชันหรืออาเรย์ numpy
ทำเครื่องหมาย

1
@ มาร์คที่น่ากลัว ขอบคุณ มีประโยชน์เมื่อคุณทราบชนิดของค่าที่ไม่สามารถจัดลำดับได้เหล่านั้นเช่นวันที่
Kishor Pawar

2
ทำไมชีวิตฉันถึงไม่รู้เรื่องนี้มาตลอด :)
Arel

1
@jjmontes ไม่ทำงานกับทุกอย่างเช่นjson.dumps({():1,type(None):2},default=str)raises TypeErrorไม่มีประเภทหรือ tuple
alancalvitti

443

การสร้างคำตอบอื่น ๆ เป็นวิธีแก้ปัญหาที่ง่ายขึ้นอยู่กับ serializer เฉพาะที่เพียงแค่แปลงdatetime.datetimeและdatetime.dateวัตถุเป็นสตริง

from datetime import date, datetime

def json_serial(obj):
    """JSON serializer for objects not serializable by default json code"""

    if isinstance(obj, (datetime, date)):
        return obj.isoformat()
    raise TypeError ("Type %s not serializable" % type(obj))

เท่าที่เห็นรหัสเพิ่งตรวจสอบเพื่อดูว่าวัตถุเป็นของคลาสdatetime.datetimeหรือdatetime.dateไม่และจากนั้นใช้.isoformat()ในการผลิตรุ่นต่อเนื่องของมันตามรูปแบบ ISO 8601, YYYY-MM-DDTHH: MM: SS (ซึ่งถอดรหัสได้ง่ายโดย JavaScript ) หากต้องการการแทนค่าแบบอนุกรมที่ซับซ้อนยิ่งขึ้นสามารถใช้รหัสอื่นแทน str () (ดูคำตอบอื่น ๆ สำหรับคำถามนี้เพื่อดูตัวอย่าง) รหัสสิ้นสุดโดยการเพิ่มข้อยกเว้นเพื่อจัดการกับกรณีที่มันถูกเรียกด้วยประเภทที่ไม่ต่อเนื่อง

ฟังก์ชัน json_serial นี้สามารถใช้ได้ดังนี้:

from datetime import datetime
from json import dumps

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

รายละเอียดเกี่ยวกับวิธีการพารามิเตอร์เริ่มต้น json.dumps งานสามารถพบได้ในการใช้มาตราพื้นฐานของเอกสารโมดูล JSON


5
ใช่คำตอบที่ถูกต้องนำเข้า datetime สวยมากขึ้นและถ้า isinstance (obj, datetime.datetime) ฉันสูญเสียหลายครั้งเพราะไม่ได้ใช้จาก datetime นำเข้าวันที่และเวลาขอบคุณ
Sérgio

12
แต่นี่ไม่ได้อธิบายถึงวิธีการยกเลิกการ deserialise ด้วยประเภทที่ถูกต้องไม่ได้หรือไม่
BlueTrin

2
ไม่ @ BlueTrin ไม่มีอะไรพูดถึงเรื่องนั้น ในกรณีของฉันฉันเลิกใช้งานจาวาสคริปต์ซึ่งใช้ได้นอกกรอบ
jgbarah

1
สิ่งนี้จะทำให้เกิดพฤติกรรมที่ไม่คาดคิดหากโมดูล json อัปเดตเพื่อรวมอนุกรมของวัตถุวันที่และเวลา
Justin

1
@serg แต่การแปลงเวลาเป็น UTC จะรวมกัน01:00:00+01:00และ02:00:00+00:00ไม่ควรจะเหมือนกันขึ้นอยู่กับบริบท พวกเขาอ้างถึงจุดเดียวกันในช่วงเวลาของหลักสูตร แต่การชดเชยอาจเป็นลักษณะที่เกี่ยวข้องของค่า
Alfe

211

ฉันเพิ่งพบปัญหานี้และทางออกของฉันคือ subclass json.JSONEncoder:

from datetime import datetime
import json

class DateTimeEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, datetime):
            return o.isoformat()

        return json.JSONEncoder.default(self, o)

ในการโทรของคุณทำสิ่งที่ชอบ: ผมได้จากหนึ่งในคำตอบดังกล่าวข้างต้นjson.dumps(yourobj, cls=DateTimeEncoder).isoformat()


22
upped เนื่องจากการใช้ JSONEncoder ที่กำหนดเองควรเป็นวิธีที่เหมาะสมในการไป
3k-

25
นี่ควรเป็นคำตอบที่ดีที่สุดซึ่งควรเป็นส่วนหนึ่งของโปรแกรมเข้ารหัส json ปกติ ถ้าเพียง แต่ถอดรหัสเป็นคลุมเครือน้อย ..
Joost

4
สำหรับผู้ที่ใช้ Django DjangoJSONEncoderดู docs.djangoproject.com/en/dev/topics/serialization/…
S. Kirby

4
มีประโยชน์สุด ๆ บรรทัดสุดท้ายอาจเป็นreturn super(DateTimeEncoder, self).default(o)
Bob Stein

16
ด้วย Python 3 บรรทัดสุดท้ายนั้นเรียบง่ายยิ่งขึ้น:return super().default(o)
ariddell

124

แปลงวันที่เป็นสตริง

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

10
และฉันจะ deserialize ในงูใหญ่ได้อย่างไร
wobmene

62
ปัญหาคือถ้าคุณมีวัตถุ datetime จำนวนมากฝังอยู่ในโครงสร้างข้อมูลหรือพวกเขาเป็นแบบสุ่ม นี่ไม่ใช่วิธีการที่เชื่อถือได้
Rebs

3
เพื่อ oDate = datetime.datetime.strptime(sDate, '%Y-%m-%d %H:%M:%S.%f')deserialize: รูปแบบที่ได้รับจาก: docs.python.org/2/library/datetime.html
Roman

13
ลดลงเนื่องจากไม่สนใจข้อมูลเขตเวลา โปรดทราบว่า.now()ใช้เวลาท้องถิ่นโดยไม่ระบุสิ่งนี้ .utcnow()ควรใช้อย่างน้อย(และ +0000 หรือ Z ต่อท้าย)
Daniel F

1
@DanielF At least .utcnow() should be usedไม่ว่าdatetime.now(timezone.utc)จะแนะนำให้ดูที่คำเตือนใน: docs.python.org/3.8/library/...
Toreno96

79

สำหรับคนอื่น ๆ ที่ไม่ต้องการหรือต้องการใช้ไลบรารี pymongo สำหรับสิ่งนี้ .. คุณสามารถบรรลุการแปลง datetime JSON ได้อย่างง่ายดายด้วยตัวอย่างเล็ก ๆ นี้:

def default(obj):
    """Default JSON serializer."""
    import calendar, datetime

    if isinstance(obj, datetime.datetime):
        if obj.utcoffset() is not None:
            obj = obj - obj.utcoffset()
        millis = int(
            calendar.timegm(obj.timetuple()) * 1000 +
            obj.microsecond / 1000
        )
        return millis
    raise TypeError('Not sure how to serialize %s' % (obj,))

จากนั้นใช้งานเช่น:

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

เอาท์พุท: 

'1365091796124'

1
ไม่ควรmillis=เยื้องในคำสั่ง if? นอกจากนี้ยังควรใช้ str (obj) เพื่อให้ได้รูปแบบ ISO ซึ่งฉันคิดว่าเป็นเรื่องปกติมากกว่า
Rebs

ทำไมคุณต้องการให้เยื้อง ตัวอย่างนี้ใช้งานได้และผลลัพธ์ที่ได้สามารถถูกดีซีเรียลไลซ์ / แยกวิเคราะห์ได้ง่ายจากจาวาสคริปต์
Jay Taylor

5
เนื่องจาก obj อาจไม่ใช่วัตถุ [เวลา, วันที่, วันที่และเวลา]
Rebs

2
ตัวอย่างของคุณไม่ถูกต้องหากเขตเวลาท้องถิ่นมีออฟเซ็ต UTC ที่ไม่เป็นศูนย์ (ส่วนใหญ่) datetime.now()ส่งคืนเวลาท้องถิ่น (เป็นวัตถุ datetime ที่ไร้เดียงสา) แต่โค้ดของคุณจะถือว่าobjเป็น UTC หากไม่ทราบเขตเวลา ใช้datetime.utcnow()แทน
jfs

1
ปรับมันจะยกข้อผิดพลาดประเภทถ้า obj ไม่รู้จักตามคำแนะนำของเอกสารหลามที่docs.python.org/2/library/json.html#basic-usage
เจย์เทย์เลอร์

40

นี่คือทางออกของฉัน:

# -*- coding: utf-8 -*-
import json


class DatetimeEncoder(json.JSONEncoder):
    def default(self, obj):
        try:
            return super(DatetimeEncoder, obj).default(obj)
        except TypeError:
            return str(obj)

จากนั้นคุณสามารถใช้งานได้เช่น:

json.dumps(dictionnary, cls=DatetimeEncoder)

ตกลง. ดีกว่ามากอย่างน้อยก็จากบริบท MongoDB คุณสามารถทำได้isinstance(obj, datetime.datetime)ภายใน TypeError เพิ่มประเภทมากขึ้นในการจับและจบลงด้วยหรือstr(obj) repr(obj)และการทิ้งทุกอย่างของคุณสามารถชี้ไปที่คลาสเฉพาะนี้ได้
JL Peyret

@Natim วิธีนี้ดีที่สุด +1
Ray

20

ฉันมีแอปพลิเคชันที่มีปัญหาคล้ายกัน วิธีการของฉันคือการ JSONize ค่าวันที่และเวลาเป็นรายการ 6 รายการ (ปี, เดือน, วัน, ชั่วโมง, นาที, วินาที); คุณสามารถไปที่ microseconds เป็นรายการ 7 รายการ แต่ฉันไม่จำเป็นต้อง:

class DateTimeEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            encoded_object = list(obj.timetuple())[0:6]
        else:
            encoded_object =json.JSONEncoder.default(self, obj)
        return encoded_object

sample = {}
sample['title'] = "String"
sample['somedate'] = datetime.datetime.now()

print sample
print json.dumps(sample, cls=DateTimeEncoder)

ผลิต:

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

ไม่สามารถทำงานได้หากบันทึกเวลาที่บันทึกไว้โดยทำ datetime.utcnow ()
saurshaz

1
คุณเห็นข้อผิดพลาดอะไรกับ datetime.utcnow () มันใช้งานได้ดีสำหรับฉัน
codingatty

17

วิธีแก้ปัญหาของฉัน (ฉันคิดฟุ้งซ่านน้อยกว่า):

def default(o):
    if type(o) is datetime.date or type(o) is datetime.datetime:
        return o.isoformat()

def jsondumps(o):
    return json.dumps(o, default=default)

จากนั้นใช้แทนjsondumps json.dumpsมันจะพิมพ์:

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

ผมที่คุณต้องการในภายหลังคุณสามารถเพิ่มกรณีพิเศษอื่น ๆ นี้กับบิดที่เรียบง่ายของdefaultวิธีการ ตัวอย่าง:

def default(o):
    if type(o) is datetime.date or type(o) is datetime.datetime:
        return o.isoformat()
    if type(o) is decimal.Decimal:
        return float(o)

1
คุณควรใช้ isinstance (o, (datetime.date, datetime.datetime,)) อาจจะไม่เจ็บที่จะรวม datetime.time เกินไป
Rebs

ฉันไม่คิดว่านี่จะเป็นทางออกที่ดีอีกต่อไป การแปลงอาจเกิดขึ้นได้มากกว่าและเป็นสถานที่ที่เข้าใจได้ง่ายขึ้นในโค้ดของคุณดังนั้นคุณจึงรู้ว่าคุณกำลังแปลงอะไรเมื่อคุณใส่สิ่งต่าง ๆ ลงในฐานข้อมูลหรืออะไรก็ตามแทนที่จะทำทุกอย่างโดย ฟังก์ชั่นโปร่งใส แต่ฉันไม่รู้
fiatjaf

1
JSON นั้นดีสำหรับการทำให้ข้อมูลเป็นอนุกรมสำหรับการประมวลผลในภายหลัง คุณอาจไม่รู้ว่าข้อมูลนั้นคืออะไร และคุณไม่จำเป็นต้องทำ การทำให้เป็นอันดับ JSON ควรจะทำงาน เช่นเดียวกับการแปลง Unicode เป็น Ascii ควร การที่ Python ไม่สามารถทำสิ่งนี้โดยไม่มีฟังก์ชั่นที่คลุมเครือทำให้มันน่ารำคาญที่จะใช้ การตรวจสอบฐานข้อมูลเป็นปัญหา IMO แยกต่างหาก
Rebs

ไม่ไม่ควร "ทำงานได้" หากคุณไม่ทราบวิธีการทำให้เป็นอันดับและการเข้าถึงข้อมูลในภายหลังจากโปรแกรม / ภาษาอื่นแสดงว่าคุณสูญเสีย
fiatjaf

2
JSON มักใช้สำหรับสตริง, ints, ลอย, วันที่ (ฉันแน่ใจว่าคนอื่นใช้สกุลเงิน, อุณหภูมิ, โดยทั่วไปด้วย) แต่วันที่และเวลาเป็นส่วนหนึ่งของไลบรารีมาตรฐานและควรสนับสนุน de / serialization หากไม่ใช่สำหรับคำถามนี้ฉันยังคงค้นหาด้วยตนเอง json blobs ที่ซับซ้อนอย่างไม่น่าเชื่อ (ซึ่งฉันไม่ได้สร้างโครงสร้างสำหรับ) สำหรับวันที่และต่อเนื่องกันเป็นลำดับที่ 1 โดย 1
Rebs

16

Q นี้ทำซ้ำครั้งแล้วครั้งเล่า - วิธีง่ายๆในการแก้ไขโมดูล json เพื่อให้ซีเรียลไลซ์เซชั่นรองรับ datetime

import json
import datetime

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

มากกว่าใช้การทำให้เป็นอนุกรม json อย่างที่คุณเคยทำ - คราวนี้ด้วย datetime ที่ถูกทำให้เป็นอนุกรมเป็น isoformat

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

ผลลัพธ์ใน: '{"สร้าง": "2015-08-26T14: 21: 31.853855"}'

ดูรายละเอียดเพิ่มเติมและคำเตือนบางอย่างได้ที่: StackOverflow: วันที่และเวลา JSON ระหว่าง Python และ JavaScript


FTW แพทช์ลิง สิ่งที่น่ารังเกียจคือแน่นอนว่าสิ่งนี้จะปรับเปลี่ยนพฤติกรรมของโมดูล json ในแอปพลิเคชันทั้งหมดของคุณซึ่งอาจทำให้คนอื่นประหลาดใจในแอปพลิเคชั่นขนาดใหญ่ดังนั้นโดยทั่วไปควรใช้ร่วมกับการดูแล imho
Jaap Versteegh

15

วิธีการ json.dumps สามารถยอมรับพารามิเตอร์ทางเลือกที่เรียกว่าค่าเริ่มต้นซึ่งคาดว่าจะเป็นฟังก์ชั่น ทุกครั้งที่ JSON พยายามแปลงค่ามันไม่ทราบวิธีการแปลงมันจะเรียกฟังก์ชั่นที่เราส่งให้ ฟังก์ชั่นจะได้รับวัตถุที่เป็นปัญหาและคาดว่าจะส่งคืนการเป็นตัวแทนของ JSON ของวัตถุ

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

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

14

หากคุณใช้ python3.7 แสดงว่าทางออกที่ดีที่สุดคือใช้ datetime.isoformat()และ datetime.fromisoformat(); มันทำงานกับdatetimeวัตถุที่ไร้เดียงสาและมีสติ:

#!/usr/bin/env python3.7

from datetime import datetime
from datetime import timezone
from datetime import timedelta
import json

def default(obj):
    if isinstance(obj, datetime):
        return { '_isoformat': obj.isoformat() }
    return super().default(obj)

def object_hook(obj):
    _isoformat = obj.get('_isoformat')
    if _isoformat is not None:
        return datetime.fromisoformat(_isoformat)
    return obj

if __name__ == '__main__':
    #d = { 'now': datetime(2000, 1, 1) }
    d = { 'now': datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=-8))) }
    s = json.dumps(d, default=default)
    print(s)
    print(d == json.loads(s, object_hook=object_hook))

เอาท์พุท:

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

หากคุณใช้ python3.6 หรือต่ำกว่าและคุณสนใจเฉพาะค่าเวลา (ไม่ใช่เขตเวลา) คุณสามารถใช้datetime.timestamp()และ datetime.fromtimestamp()แทนได้

ถ้าคุณใช้ python3.6 หรือต่ำกว่าและคุณสนใจเขตเวลาคุณก็สามารถใช้งานได้datetime.tzinfoแต่คุณต้องทำให้เป็นเขตข้อมูลนี้ด้วยตัวเอง วิธีที่ง่ายที่สุดในการทำเช่นนี้คือการเพิ่มฟิลด์อื่น_tzinfoในวัตถุที่เป็นอนุกรม

ในที่สุดระวัง precensions ในตัวอย่างเหล่านี้ทั้งหมด


datetime.isoformat () มีอยู่ใน Python 2.7: docs.python.org/2/library/ …
powlo

11

คุณควรใช้.strftime()วิธีการใน.datetime.now()การทำให้เป็นวิธีการที่ต่อเนื่องกันได้

นี่คือตัวอย่าง:

from datetime import datetime

time_dict = {'time': datetime.now().strftime('%Y-%m-%dT%H:%M:%S')}
sample_dict = {'a': 1, 'b': 2}
sample_dict.update(time_dict)
sample_dict

เอาท์พุท:

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

10

นี่เป็นวิธีง่ายๆในการแก้ปัญหา "วันที่และเวลาไม่ผ่าน JSON"

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)

เอาท์พุท: -> {"วันที่": "2015-12-16T04: 48: 20.024609"}


8

คุณจะต้องจัดหาระดับการเข้ารหัสที่กำหนดเองกับพารามิเตอร์ของcls json.dumpsอ้างจากเอกสาร :

>>> import json
>>> class ComplexEncoder(json.JSONEncoder):
...     def default(self, obj):
...         if isinstance(obj, complex):
...             return [obj.real, obj.imag]
...         return json.JSONEncoder.default(self, obj)
...
>>> dumps(2 + 1j, cls=ComplexEncoder)
'[2.0, 1.0]'
>>> ComplexEncoder().encode(2 + 1j)
'[2.0, 1.0]'
>>> list(ComplexEncoder().iterencode(2 + 1j))
['[', '2.0', ', ', '1.0', ']']

ตัวอย่างนี้ใช้ตัวเลขที่ซับซ้อนเป็นตัวอย่าง แต่คุณสามารถสร้างคลาสเพื่อเข้ารหัสวันที่ได้อย่างง่ายดาย (ยกเว้นฉันคิดว่า JSON ค่อนข้างคลุมเครือเกี่ยวกับวันที่)


5

วิธีที่ง่ายที่สุดในการทำเช่นนี้คือเปลี่ยนส่วนของ dict ที่อยู่ในรูปแบบวันที่และเวลาเป็น isoformat ค่านั้นจะเป็นสตริงใน isoformat ที่ json ไม่เป็นผล

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

5

จริงๆแล้วมันค่อนข้างง่าย หากคุณต้องการทำให้เป็นอันดับวันบ่อย ๆ ให้ทำงานกับมันเป็นสตริง คุณสามารถแปลงกลับเป็นวัตถุ datetime ได้อย่างง่ายดายหากต้องการ

หากคุณต้องการทำงานเป็นวัตถุ datetime เป็นส่วนใหญ่ให้แปลงเป็นสตริงก่อนทำการจัดลำดับ

import json, datetime

date = str(datetime.datetime.now())
print(json.dumps(date))
"2018-12-01 15:44:34.409085"
print(type(date))
<class 'str'>

datetime_obj = datetime.datetime.strptime(date, '%Y-%m-%d %H:%M:%S.%f')
print(datetime_obj)
2018-12-01 15:44:34.409085
print(type(datetime_obj))
<class 'datetime.datetime'>

อย่างที่คุณเห็นเอาท์พุทเหมือนกันในทั้งสองกรณี ประเภทเท่านั้นจะแตกต่างกัน


3

หากคุณใช้ผลลัพธ์ในมุมมองอย่าลืมตอบกลับอย่างเหมาะสม ตาม API jsonify ทำสิ่งต่อไปนี้:

สร้างการตอบสนองด้วยการเป็นตัวแทน JSON ของข้อโต้แย้งที่กำหนดด้วย application / json mimetype

ในการเลียนแบบพฤติกรรมนี้ด้วย json.dumps คุณต้องเพิ่มโค้ดพิเศษอีกสองสามบรรทัด

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

คุณควรส่งคืน dict เพื่อทำซ้ำการตอบสนองของ jsonify อย่างสมบูรณ์ ดังนั้นไฟล์ทั้งหมดจะมีลักษณะเช่นนี้

from flask import make_response
from json import JSONEncoder, dumps


class CustomEncoder(JSONEncoder):
    def default(self, obj):
        if set(['quantize', 'year']).intersection(dir(obj)):
            return str(obj)
        elif hasattr(obj, 'next'):
            return list(obj)
        return JSONEncoder.default(self, obj)

@app.route('/get_reps/', methods=['GET'])
def get_reps():
    sample = ['some text', <datetime object>, 123]
    response = make_response(dumps({'result': sample}, cls=CustomEncoder))
    response.headers['Content-Type'] = 'application/json'
    response.headers['mimetype'] = 'application/json'
    return response

1
คำถามไม่มีส่วนเกี่ยวข้องกับกระติกน้ำ
Zoran Pavlovic

2
คำถามเกี่ยวกับหลาม คำตอบของฉันแก้คำถามโดยใช้หลาม OP ไม่ได้บอกว่าวิธีการแก้ไขควรรวมหรือแยกไลบรารีบางอย่าง pymongoนอกจากนี้ยังเป็นประโยชน์สำหรับคนอื่นอ่านคำถามนี้ที่ต้องการทางเลือกไปยัง
reubano

คำถามเหล่านี้เกี่ยวกับ Python และไม่เกี่ยวกับ Flask กระติกน้ำไม่จำเป็นต้องใช้ในการตอบคำถามของคุณดังนั้นฉันขอแนะนำให้คุณลบออก
Zoran Pavlovic

3

ลองอันนี้พร้อมตัวอย่างเพื่อวิเคราะห์คำ:

#!/usr/bin/env python

import datetime
import json

import dateutil.parser  # pip install python-dateutil


class JSONEncoder(json.JSONEncoder):

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


def test():
    dts = [
        datetime.datetime.now(),
        datetime.datetime.now(datetime.timezone(-datetime.timedelta(hours=4))),
        datetime.datetime.utcnow(),
        datetime.datetime.now(datetime.timezone.utc),
    ]
    for dt in dts:
        dt_isoformat = json.loads(json.dumps(dt, cls=JSONEncoder))
        dt_parsed = dateutil.parser.parse(dt_isoformat)
        assert dt == dt_parsed
        print(f'{dt}, {dt_isoformat}, {dt_parsed}')
        # 2018-07-22 02:22:42.910637, 2018-07-22T02:22:42.910637, 2018-07-22 02:22:42.910637
        # 2018-07-22 02:22:42.910643-04:00, 2018-07-22T02:22:42.910643-04:00, 2018-07-22 02:22:42.910643-04:00
        # 2018-07-22 06:22:42.910645, 2018-07-22T06:22:42.910645, 2018-07-22 06:22:42.910645
        # 2018-07-22 06:22:42.910646+00:00, 2018-07-22T06:22:42.910646+00:00, 2018-07-22 06:22:42.910646+00:00


if __name__ == '__main__':
    test()

2

ทางออกของฉัน ...

from datetime import datetime
import json

from pytz import timezone
import pytz


def json_dt_serializer(obj):
    """JSON serializer, by macm.
    """
    rsp = dict()
    if isinstance(obj, datetime):
        rsp['day'] = obj.day
        rsp['hour'] = obj.hour
        rsp['microsecond'] = obj.microsecond
        rsp['minute'] = obj.minute
        rsp['month'] = obj.month
        rsp['second'] = obj.second
        rsp['year'] = obj.year
        rsp['tzinfo'] = str(obj.tzinfo)
        return rsp
    raise TypeError("Type not serializable")


def json_dt_deserialize(obj):
    """JSON deserialize from json_dt_serializer, by macm.
    """
    if isinstance(obj, str):
        obj = json.loads(obj)
    tzone = timezone(obj['tzinfo'])
    tmp_dt = datetime(obj['year'],
                      obj['month'],
                      obj['day'],
                      hour=obj['hour'],
                      minute=obj['minute'],
                      second=obj['second'],
                      microsecond=obj['microsecond'])
    loc_dt = tzone.localize(tmp_dt)
    deserialize = loc_dt.astimezone(tzone)
    return deserialize    

ตกลงตอนนี้มีการทดสอบ

# Tests
now = datetime.now(pytz.utc)

# Using this solution
rsp = json_dt_serializer(now)
tmp = json_dt_deserialize(rsp)
assert tmp == now
assert isinstance(tmp, datetime) == True
assert isinstance(now, datetime) == True

# using default from json.dumps
tmp = json.dumps(datetime.now(pytz.utc), default=json_dt_serializer)
rsp = json_dt_deserialize(tmp)
assert isinstance(rsp, datetime) == True

# Lets try another timezone
eastern = timezone('US/Eastern')
now = datetime.now(eastern)
rsp = json_dt_serializer(now)
tmp = json_dt_deserialize(rsp)

print(tmp)
# 2015-10-22 09:18:33.169302-04:00

print(now)
# 2015-10-22 09:18:33.169302-04:00

# Wow, Works!
assert tmp == now

2

นี่คือโซลูชันที่สมบูรณ์สำหรับการแปลงวันที่และเวลาเป็น JSON และย้อนกลับ ..

import calendar, datetime, json

def outputJSON(obj):
    """Default JSON serializer."""

    if isinstance(obj, datetime.datetime):
        if obj.utcoffset() is not None:
            obj = obj - obj.utcoffset()

        return obj.strftime('%Y-%m-%d %H:%M:%S.%f')
    return str(obj)

def inputJSON(obj):
    newDic = {}

    for key in obj:
        try:
            if float(key) == int(float(key)):
                newKey = int(key)
            else:
                newKey = float(key)

            newDic[newKey] = obj[key]
            continue
        except ValueError:
            pass

        try:
            newDic[str(key)] = datetime.datetime.strptime(obj[key], '%Y-%m-%d %H:%M:%S.%f')
            continue
        except TypeError:
            pass

        newDic[str(key)] = obj[key]

    return newDic

x = {'Date': datetime.datetime.utcnow(), 34: 89.9, 12.3: 90, 45: 67, 'Extra': 6}

print x

with open('my_dict.json', 'w') as fp:
    json.dump(x, fp, default=outputJSON)

with open('my_dict.json') as f:
    my_dict = json.load(f, object_hook=inputJSON)

print my_dict

เอาท์พุต

{'Date': datetime.datetime(2013, 11, 8, 2, 30, 56, 479727), 34: 89.9, 45: 67, 12.3: 90, 'Extra': 6}
{'Date': datetime.datetime(2013, 11, 8, 2, 30, 56, 479727), 34: 89.9, 45: 67, 12.3: 90, 'Extra': 6}

ไฟล์ JSON

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

สิ่งนี้ทำให้ฉันสามารถนำเข้าและส่งออกสตริง, ints, ลอยและวัตถุวันที่และเวลา ไม่ควรที่จะขยายประเภทอื่นอย่างหนัก


1
มันจะระเบิดในหลาม TypeError: 'str' does not support the buffer interface3 มันเป็นเพราะโหมดเปิดควรจะเป็น'wb' 'w'นอกจากนี้ยังพัดในการดีซีเรียลไลเซชันเมื่อเรามีข้อมูลคล้ายกับวันที่เหมือน'0000891618-05-000338'แต่ไม่ใช่รูปแบบการจับคู่
omikron

2

แปลงdate ไป string

date = str(datetime.datetime(somedatetimehere)) 

คำตอบ jjmontes ทำตรงนั้น แต่ไม่จำเป็นต้องทำอย่างชัดเจนสำหรับทุก ๆ วัน ...
bluesummers

2

โดยทั่วไปมีหลายวิธีในการจัดเรียงชุดข้อมูลเช่น:

  1. สตริง ISO สั้นและสามารถรวมข้อมูลเขตเวลาเช่นคำตอบของ @ jgbarah
  2. การประทับเวลา (ข้อมูลเขตเวลาหายไป) เช่นคำตอบของ @ JayTaylor
  3. พจนานุกรมคุณสมบัติ (รวมถึงเขตเวลา)

หากคุณไม่เป็นไรด้วยวิธีสุดท้ายแพ็คเกจjson_tricksจะจัดการวันที่เวลาและชุดข้อมูลรวมถึงเขตเวลา

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

ซึ่งจะช่วยให้:

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

ดังนั้นสิ่งที่คุณต้องทำคือ

`pip install json_tricks`

แล้วนำเข้าจากแทนjson_tricksjson

ข้อดีของการไม่จัดเก็บเป็นสตริงเดี่ยว, int หรือ float มาเมื่อถอดรหัส: หากคุณพบเพียงแค่สตริงหรือโดยเฉพาะอย่างยิ่ง int หรือลอยคุณจำเป็นต้องรู้บางสิ่งบางอย่างเกี่ยวกับข้อมูลที่จะรู้ว่ามันเป็นวันที่และเวลา คุณสามารถจัดเก็บข้อมูลเมตาเพื่อให้สามารถถอดรหัสได้โดยอัตโนมัติซึ่งเป็นสิ่งที่json_tricksเหมาะกับคุณ นอกจากนี้ยังสามารถแก้ไขได้อย่างง่ายดายสำหรับมนุษย์

คำเตือน: มันทำโดยฉัน เพราะฉันมีปัญหาเดียวกัน


1

ฉันได้รับข้อความแสดงข้อผิดพลาดเดียวกันในขณะที่เขียนมัณฑนากรต่อเนื่องภายใน Class ด้วย sqlalchemy ดังนั้นแทนที่จะ:

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

ฉันเพียงแค่ยืมความคิดของ jgbarah ในการใช้ isoformat () และต่อท้ายค่าดั้งเดิมด้วย isoformat () เพื่อให้ดูเหมือนตอนนี้:

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

1

การแก้ไขอย่างรวดเร็วหากคุณต้องการการจัดรูปแบบของคุณเอง

for key,val in sample.items():
    if isinstance(val, datetime):
        sample[key] = '{:%Y-%m-%d %H:%M:%S}'.format(val) #you can add different formating here
json.dumps(sample)

1

หากคุณอยู่ทั้งสองด้านของการสื่อสารคุณสามารถใช้ฟังก์ชั่นrepr ()และeval ()พร้อมกับ json

import datetime, json

dt = datetime.datetime.now()
print("This is now: {}".format(dt))

dt1 = json.dumps(repr(dt))
print("This is serialised: {}".format(dt1))

dt2 = json.loads(dt1)
print("This is loaded back from json: {}".format(dt2))

dt3 = eval(dt2)
print("This is the same object as we started: {}".format(dt3))

print("Check if they are equal: {}".format(dt == dt3))

คุณไม่ควรนำเข้าวันที่และเวลาเป็น

from datetime import datetime

ตั้งแต่ eval จะบ่น หรือคุณสามารถส่งวันที่และเวลาเป็นพารามิเตอร์เพื่อให้ Eval ไม่ว่าจะในกรณีใด ๆ


0

ฉันพบปัญหาเดียวกันเมื่อทำการจำลองวัตถุ django ภายนอกเพื่อถ่ายโอนข้อมูลเป็น JSON นี่คือวิธีที่คุณสามารถแก้ปัญหาได้

def externalize(model_obj):
  keys = model_obj._meta.get_all_field_names() 
  data = {}
  for key in keys:
    if key == 'date_time':
      date_time_obj = getattr(model_obj, key)
      data[key] = date_time_obj.strftime("%A %d. %B %Y")
    else:
      data[key] = getattr(model_obj, key)
  return data

0
def j_serial(o):     # self contained
    from datetime import datetime, date
    return str(o).split('.')[0] if isinstance(o, (datetime, date)) else None

การใช้งานของยูทิลิตี้ข้างต้น:

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

0

ห้องสมุดนี้superjsonสามารถทำมันได้ และคุณสามารถกำหนดเอง JSON serializer สำหรับวัตถุหลามของคุณเองโดยทำตามคำแนะนำนี้https://superjson.readthedocs.io/index.html#extend

แนวคิดทั่วไปคือ:

รหัสของคุณจำเป็นต้องค้นหาวิธีการซีเรียลไลซ์เซชั่น / การดีซีเรียลไลเซชันที่ถูกต้องตามวัตถุหลาม โดยปกติชื่อคลาสแบบเต็มเป็นตัวระบุที่ดี

และจากนั้นวิธี ser / deser ของคุณควรจะสามารถแปลงวัตถุของคุณเป็นวัตถุ Json ที่สามารถทำให้เป็นอนุกรมได้ซึ่งเป็นการรวมกันของประเภทหลามทั่วไป, dict, รายการ, สตริง, int, float และใช้วิธีการของคุณ deser ย้อนกลับ


-1

ฉันอาจไม่ถูกต้อง 100% แต่นี่เป็นวิธีง่ายๆในการทำให้เป็นอันดับ

#!/usr/bin/python
import datetime,json

sampledict = {}
sampledict['a'] = "some string"
sampledict['b'] = datetime.datetime.now()

print sampledict   # output : {'a': 'some string', 'b': datetime.datetime(2017, 4, 15, 5, 15, 34, 652996)}

#print json.dumps(sampledict)

'''
output : 

Traceback (most recent call last):
  File "./jsonencodedecode.py", line 10, in <module>
    print json.dumps(sampledict)
  File "/usr/lib/python2.7/json/__init__.py", line 244, in dumps
    return _default_encoder.encode(obj)
  File "/usr/lib/python2.7/json/encoder.py", line 207, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib/python2.7/json/encoder.py", line 270, in iterencode
    return _iterencode(o, 0)
  File "/usr/lib/python2.7/json/encoder.py", line 184, in default
    raise TypeError(repr(o) + " is not JSON serializable")
TypeError: datetime.datetime(2017, 4, 15, 5, 16, 17, 435706) is not JSON serializable


'''

sampledict['b'] = datetime.datetime.now().strftime("%B %d, %Y %H:%M %p")

afterdump = json.dumps(sampledict)

print afterdump  #output : {"a": "some string", "b": "April 15, 2017 05:18 AM"}

print type(afterdump) #<type 'str'>


afterloads = json.loads(afterdump) 

print afterloads # output : {u'a': u'some string', u'b': u'April 15, 2017 05:18 AM'}


print type(afterloads) # output :<type 'dict'> 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.