web-dev-qa-db-ja.com

PythonとJavaScriptの間のJSON日時

Pythonから JSON を使用してdatetime.datetimeオブジェクトをシリアライズ形式で送信し、JSONを使用してJavaScriptでデシリアライズしたいと思います。これを行うための最良の方法は何ですか?

383
kevin

これを処理するためにjson.dumpsに 'default'パラメータを追加することができます。

date_handler = lambda obj: (
    obj.isoformat()
    if isinstance(obj, (datetime.datetime, datetime.date))
    else None
)
json.dumps(datetime.datetime.now(), default=date_handler)
'"2010-04-20T20:08:21.634121"'

これは ISO 8601 形式です。

より包括的なデフォルトハンドラ関数

def handler(obj):
    if hasattr(obj, 'isoformat'):
        return obj.isoformat()
    Elif isinstance(obj, ...):
        return ...
    else:
        raise TypeError, 'Object of type %s with value of %s is not JSON serializable' % (type(obj), repr(obj))

更新:値だけでなくタイプの出力を追加しました。
更新:日付も処理します

366
JT.

クロスランゲージプロジェクトでは、 RfC 3339 の日付を含む文字列が最適な方法です。 RfC 3339の日付は次のようになります。

  1985-04-12T23:20:50.52Z

フォーマットの大部分は明らかだと思います。唯一のちょっと変わったことは最後の "Z"かもしれません。 GMT/UTCの略です。 CEST(夏はドイツ)に+02:00のようなタイムゾーンオフセットを追加することもできます。個人的には、表示されるまですべてをUTCで保存することを好みます。

表示、比較、保存のために、あなたはすべての言語にわたって文字列フォーマットでそれを残すことができます。計算のために簡単に日付を必要とする場合は、ほとんどの言語でネイティブの日付オブジェクトに変換し直すことができます。

それで、このようにJSONを生成してください:

  json.dump(datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ'))

残念ながら、JavascriptのDateコンストラクタはRfC 3339文字列を受け付けませんが、インターネット上で入手可能な パーサ が多数あります。

huTools.hujson タイムゾーンを正しく処理しながら、date/datetimeオブジェクトを含むPythonコードで発生する可能性がある最も一般的なエンコーディング問題を処理しようとします。

76
max

私はそれを解決しました。

Datetime.now()で作成されたPythonのdatetimeオブジェクトdがあるとしましょう。その値は以下のとおりです。

datetime.datetime(2011, 5, 25, 13, 34, 5, 787000)

ISO 8601の日時文字列としてJSONにシリアル化できます。

import json    
json.dumps(d.isoformat())

Datetimeオブジェクトの例は次のようにシリアル化されます。

'"2011-05-25T13:34:05.787000"'

この値は、Javascriptレイヤで受け取ると、Dateオブジェクトを作成できます。

var d = new Date("2011-05-25T13:34:05.787000");

Javascript 1.8.5以降、DateオブジェクトにはtoJSONメソッドがあり、これは標準フォーマットの文字列を返します。したがって、上記のJavascriptオブジェクトをJSONにシリアライズするには、コマンドは次のようになります。

d.toJSON()

これはあなたに与えるでしょう:

'2011-05-25T20:34:05.787Z'

この文字列は、Pythonで受信されると、日時オブジェクトにシリアル化し直すことができます。

datetime.strptime('2011-05-25T20:34:05.787Z', '%Y-%m-%dT%H:%M:%S.%fZ')

これにより、次のdatetimeオブジェクトが作成されます。これは、最初に起動したものと同じで、正しいものです。

datetime.datetime(2011, 5, 25, 20, 34, 5, 787000)
64
user240515

jsonを使用すると、JSONEncoderをサブクラス化し、default()メソッドをオーバーライドして独自のカスタムシリアライザを提供できます。

import json
import datetime

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

それなら、このように呼ぶことができます。

>>> DateTimeJSONEncoder().encode([datetime.datetime.now()])
'["2010-06-15T14:42:28"]'
49
ramen

これは標準ライブラリjsonモジュールを使ってdatetime.datetimeとdatetime.dateオブジェクトを再帰的にエンコードおよびデコードするためのかなり完全な解決策です。 datetime.datetime.strptime()フォーマット文字列の%fフォーマットコードはそれ以降でしかサポートされていないため、これにはPython> = 2.6が必要です。 Python 2.5をサポートするには、変換を試みる前に%fをドロップしてISOの日付文字列からマイクロ秒を取り除きますが、もちろんマイクロ秒の精度は失われます。タイムゾーン名やUTCオフセットを含む可能性のある他のソースからのISO日付文字列との相互運用性のために、変換前に日付文字列の一部を削除する必要があるかもしれません。 ISOの日付文字列(および他の多くの日付形式)の完全なパーサーについては、サードパーティの dateutil モジュールを参照してください。

ISOの日付文字列がJavaScriptのリテラルオブジェクト表記法またはオブジェクト内のネストされた構造体の値である場合にのみ、デコードは機能します。最上位配列の項目であるISO日付文字列はデコードされません。

すなわちこれは動作します:

date = datetime.datetime.now()
>>> json = dumps(dict(foo='bar', innerdict=dict(date=date)))
>>> json
'{"innerdict": {"date": "2010-07-15T13:16:38.365579"}, "foo": "bar"}'
>>> loads(json)
{u'innerdict': {u'date': datetime.datetime(2010, 7, 15, 13, 16, 38, 365579)},
u'foo': u'bar'}

そしてこれも:

>>> json = dumps(['foo', 'bar', dict(date=date)])
>>> json
'["foo", "bar", {"date": "2010-07-15T13:16:38.365579"}]'
>>> loads(json)
[u'foo', u'bar', {u'date': datetime.datetime(2010, 7, 15, 13, 16, 38, 365579)}]

しかし、これは期待通りには機能しません。

>>> json = dumps(['foo', 'bar', date])
>>> json
'["foo", "bar", "2010-07-15T13:16:38.365579"]'
>>> loads(json)
[u'foo', u'bar', u'2010-07-15T13:16:38.365579']

これがコードです:

__all__ = ['dumps', 'loads']

import datetime

try:
    import json
except ImportError:
    import simplejson as json

class JSONDateTimeEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, (datetime.date, datetime.datetime)):
            return obj.isoformat()
        else:
            return json.JSONEncoder.default(self, obj)

def datetime_decoder(d):
    if isinstance(d, list):
        pairs = enumerate(d)
    Elif isinstance(d, dict):
        pairs = d.items()
    result = []
    for k,v in pairs:
        if isinstance(v, basestring):
            try:
                # The %f format code is only supported in Python >= 2.6.
                # For Python <= 2.5 strip off microseconds
                # v = datetime.datetime.strptime(v.rsplit('.', 1)[0],
                #     '%Y-%m-%dT%H:%M:%S')
                v = datetime.datetime.strptime(v, '%Y-%m-%dT%H:%M:%S.%f')
            except ValueError:
                try:
                    v = datetime.datetime.strptime(v, '%Y-%m-%d').date()
                except ValueError:
                    pass
        Elif isinstance(v, (dict, list)):
            v = datetime_decoder(v)
        result.append((k, v))
    if isinstance(d, list):
        return [x[1] for x in result]
    Elif isinstance(d, dict):
        return dict(result)

def dumps(obj):
    return json.dumps(obj, cls=JSONDateTimeEncoder)

def loads(obj):
    return json.loads(obj, object_hook=datetime_decoder)

if __== '__main__':
    mytimestamp = datetime.datetime.utcnow()
    mydate = datetime.date.today()
    data = dict(
        foo = 42,
        bar = [mytimestamp, mydate],
        date = mydate,
        timestamp = mytimestamp,
        struct = dict(
            date2 = mydate,
            timestamp2 = mytimestamp
        )
    )

    print repr(data)
    jsonstring = dumps(data)
    print jsonstring
    print repr(loads(jsonstring))
30
Chris Arndt

JavascriptのみがJSONを使用すると確信している場合は、JavascriptのDateオブジェクトを直接渡すことをおすすめします。

datetimeオブジェクトのctime()メソッドは、Javascript Dateオブジェクトが理解できる文字列を返します。

import datetime
date = datetime.datetime.today()
json = '{"mydate":new Date("%s")}' % date.ctime()

Javascriptはそれをオブジェクトリテラルとして喜んで使用します。これでDateオブジェクトが正しく組み込まれました。

23
Triptych

ゲームの後半で... :)

非常に単純な解決策は、jsonモジュールのデフォルトにパッチを当てることです。例えば:

import json
import datetime

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

さて、あなたはjson.dumps()をいつもdatetimeをサポートしているかのように使うことができます...

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

これは、jsonモジュールのこの拡張機能を常に使用して、自分や他の人がjsonのシリアル化を使用する方法を変更したくない(既存のコードであってもなくても)必要がある場合には意味があります。

そのような方法でライブラリにパッチを適用することを悪い習慣として考えるかもしれないことに注意してください。アプリケーションを複数の方法で拡張したい場合は、特別な注意を払う必要があります。そのような場合は、ラーメンまたはJTによるソリューションを使用し、それぞれの場合に適切なjson拡張を選択することをお勧めします。

11
davidhadas

timestampを除いて、コミュニティのwiki回答に追加することはそれほど多くありません。

Javascriptは以下のフォーマットを使用します。

new Date().toJSON() // "2016-01-08T19:00:00.123Z"

Python側(json.dumpsハンドラーについては他の答えを見てください):

>>> from datetime import datetime
>>> d = datetime.strptime('2016-01-08T19:00:00.123Z', '%Y-%m-%dT%H:%M:%S.%fZ')
>>> d
datetime.datetime(2016, 1, 8, 19, 0, 0, 123000)
>>> d.isoformat() + 'Z'
'2016-01-08T19:00:00.123000Z'

Zを省略すると、Angularのようなフロントエンドフレームワークはブラウザローカルタイムゾーンに日付を表​​示できません。

> $filter('date')('2016-01-08T19:00:00.123000Z', 'yyyy-MM-dd HH:mm:ss')
"2016-01-08 20:00:00"
> $filter('date')('2016-01-08T19:00:00.123000', 'yyyy-MM-dd HH:mm:ss')
"2016-01-08 19:00:00"
6
user1338062

Python側:

import time, json
from datetime import datetime as dt
your_date = dt.now()
data = json.dumps(time.mktime(your_date.timetuple())*1000)
return data # data send to javascript

JavaScript側:

var your_date = new Date(data)

データがpythonの結果である場合

4
Sank

私のアドバイスは図書館を使うことです。 pypi.orgから入手できるものがいくつかあります。

私はこれを使用します、それはそれがうまくいく: https://pypi.python.org/pypi/asjson

4
guettli

どうやら "正しい" JSON(well JavaScript)日付フォーマット は2012-04-23T18:25:43.511Z - UTCと "Z"です。このJavaScriptがないと、文字列からDate()オブジェクトを作成するときにWebブラウザのローカルタイムゾーンが使用されます。

"素朴な"時間(Pythonが時間帯なしで時間を呼び、これがローカルであると仮定しているもの)については、次に示すように ローカルタイムゾーンを強制 してください。

def default(obj):
    if hasattr(obj, "json") and callable(getattr(obj, "json")):
        return obj.json()
    if hasattr(obj, "isoformat") and callable(getattr(obj, "isoformat")):
        # date/time objects
        if not obj.utcoffset():
            # add local timezone to "naive" local time
            # https://stackoverflow.com/questions/2720319/python-figure-out-local-timezone
            tzinfo = datetime.now(timezone.utc).astimezone().tzinfo
            obj = obj.replace(tzinfo=tzinfo)
        # convert to UTC
        obj = obj.astimezone(timezone.utc)
        # strip the UTC offset
        obj = obj.replace(tzinfo=None)
        return obj.isoformat() + "Z"
    Elif hasattr(obj, "__str__") and callable(getattr(obj, "__str__")):
        return str(obj)
    else:
        print("obj:", obj)
        raise TypeError(obj)

def dump(j, io):
    json.dump(j, io, indent=2, default=default)

なぜこれがそんなに難しいですか。

0
cagney