web-dev-qa-db-ja.com

「datetime.datetimeがJSONのシリアライズ可能ではない」を克服するにはどうすればいいですか?

私は次のように基本的な口述をします:

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になります。

538
Rolando

2018用に更新

元の答えは、MongoDBの「日付」フィールドが次のように表されていた方法に対応していました。

{"$date": 1506816000000}

datetimeをjsonにシリアライズするための一般的なPythonの解決策が欲しいなら、依存関係を必要としない素早い解決方法については @jjmontes 'answer を調べてください。


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はこの種のものを適切に扱うネイティブのDjangoJSONEncoderシリアライザを提供しています。

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

そのため、場合によってはそれに注意する必要があります。

286
jdi

他の答えに基づいて、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))

見てのとおり、このコードはobjectがdatetime.datetimedatetime.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モジュールドキュメントの/ - 基本的な使い方 にあります。

401
jgbarah

日付とすべてを食べる、私の素早い汚いJSONダンプ:

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

私はこの問題に遭遇したばかりで、私の解決策は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()私は上記の答えの一つから得ました。

181
lenny

日付を文字列に変換する

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

このために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'
77
Jay Taylor

これが私の解決策です:

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

私は同様の問題を抱えた申請書を持っています。私のアプローチは、datetime値を6項目のリスト(年、月、日、時、分、秒)としてJSON化することでした。あなたは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"}
20
codingatty

このQは何度も何度も繰り返します - シリアル化がdatetimeをサポートするようにjsonモジュールにパッチをあてる簡単な方法です。

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

結果として: '{"created": "2015-08-26T14:21:31.853855"}'

詳細と注意点については、次のURLを参照してください。 StackOverflow:PythonとJavaScript間のJSON日時

17
davidhadas

私の解決策(冗長性は少ないと思います):

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)

それからjson.dumpsの代わりにjsondumpsを使用してください。それは印刷されます:

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

これは、 "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)

出力: - > {"日付": "2015-12-16T04:48:20.024609"}

10
ob92

あなたはjson.dumpsclsパラメータでカスタムエンコーダクラスを供給しなければなりません。 docs から引用するには:

>>> 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が日付について少しあいまいであると思います)

8
Sean Redmond

シリアライズ可能 メソッドとしてそれを作るためにあなたは.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'}
7
Benyamin Jafari

Json.dumpsメソッドはdefaultと呼ばれるオプションのパラメータを受け取ることができます。これは関数であることが期待されています。 JSONが値を変換しようとするたびに、変換方法がわからないため、渡した関数が呼び出されます。この関数は問題のオブジェクトを受け取り、そのオブジェクトのJSON表現を返すことが期待されています。

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

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

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 __== '__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を追加することです。

最後に、これらすべての例で精度に注意してください。

5
Cyker

これを行う最も簡単な方法は、日時形式の辞書の一部をisoformatに変更することです。その値は、実際にはjsonが使用できるisoformatの文字列になります。

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

結果をビューで使用している場合は、必ず適切な応答を返してください。 APIによると、jsonifyは次のことを行います。

Application/json mimetypeを使用して、指定された引数のJSON表現でResponseを作成します。

この動作をjson.dumpsで模倣するには、数行のコードを追加する必要があります。

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

また、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
3
reubano

これは、datetimeを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}

これにより、文字列、整数値、浮動小数点数、および日時オブジェクトをインポートおよびエクスポートできます。他のタイプに拡張するのは難しくありません。

3
Hovo

一般に、日時を直列化する方法はいくつかあります。

  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ではなくjson_tricksからインポートします。

単一の文字列、intまたはfloatとして格納しないことの利点は、デコード時に発生します。文字列、または特にintまたはfloatに遭遇した場合は、データに関する情報を知っておく必要があります。口述として、メタデータを保存して自動的にデコードできるようにすることができます。これがjson_tricksによるものです。人間にとっても簡単に編集できます。

免責事項:それは私によって作られました。私は同じ問題を抱えていたからです。

2
Mark

それを解析するために例を使ってこれを試してみてください。

#!/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 __== '__main__':
    test()
2
zhigang

datestringに変換します

date = str(datetime.datetime(somedatetimehere)) 
2

実はそれはとても簡単です。日付をシリアル化する必要がある場合は、それらを文字列として扱います。必要に応じて、それらを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'>

ご覧のとおり、出力はどちらの場合も同じです。種類が違うだけです。

2
AngelDown

あなたがあなた自身のフォーマットを望むならば、迅速な修正

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
Arash

通信の両側にいる場合は、jsonと共に repr() および eval() 関数を使用できます。

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

以下のようにdatetimeをインポートするべきではありません。

from datetime import datetime

evalは文句を言うので。あるいは、datetimeをevalのパラメータとして渡すこともできます。いずれにせよこれはうまくいくはずです。

1
ThunderBear

私の解決策.

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
1
macm

Sqlalchemyを使ってClassの中にシリアライズ・デコレータを書いているときに同じエラー・メッセージを受け取りました。だから代わりに:

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
Treefish Zhang

JSONとしてダンプするようにDjangoモデルオブジェクトを外部化するときにも同じ問題に遭遇しました。これを解決する方法があります。

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
naren
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
Vinod Kumar

このライブラリは superjson にできます。そして、あなたは簡単にこの命令に従うことによってあなた自身のPythonオブジェクトのためのカスタムJSONシリアライザをすることができます https://superjson.readthedocs.io/index.html#extend

一般的な概念は次のとおりです。

あなたのコードはpythonオブジェクトに基づいて正しいシリアライゼーション/デシリアライゼーション方法を見つける必要があります。通常、完全なクラス名は良い識別子です。

そして、あなたのser/deserメソッドはあなたのオブジェクトを通常のJsonシリアライズ可能オブジェクト、一般的なpython型、dict、list、string、int、floatの組み合わせに変換することができるはずです。そしてあなたのより適切な方法を逆に実行してください。

0
MacSanhe