PythonとJavaScript間のJSON日時


393

JSONを使用してPythonからシリアル化された形式でdatetime.datetimeオブジェクトを送信し、JSONを使用してJavaScriptで非シリアル化したいと考えています。これを行う最良の方法は何ですか?


ライブラリを使用したいですか、それとも自分でコーディングしたいですか?
guettli

回答:


370

これを処理するために、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))

更新:タイプと値の出力が追加されました。
更新:日付も処理


11
問題は、list / dictに他のオブジェクトがある場合、このコードはそれらをNoneに変換することです。
Tomasz Wysocki

5
json.dumpsはそれらを変換する方法も知りませんが、例外は抑制されています。悲しいことに、1行のラムダ修正には欠点があります。未知数に対して例外を発生させたい場合(これは良いアイデアです)、上記で追加した関数を使用します。
JT。

9
完全な出力形式にもタイムゾーンが必要です...そしてisoformat()はこの機能を提供しません...したがって、戻る前に文字列にその情報を必ず追加してください
Nick Franceschina

3
これが最善の方法です。なぜこれが答えとして選ばれなかったのですか?
Brendon Crawford

16
TypeError例外を上げることができるので、必要に応じてラムダは、非日時種類の基本実装を呼び出すように適合させることができる:dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime) else json.JSONEncoder().default(obj)
パスカルボークを

81

言語間プロジェクトの場合、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は、タイムゾーンを正しく処理しながら、日付/日付時刻オブジェクトを含むPythonコードで遭遇する可能性のある最も一般的なエンコーディングの問題を処理しようとします。


17
この日付フォーマットメカニズムはネイティブでサポートされていdatetimeます。datetime.isoformat()とによってサポートされます。デフォルトsimplejsonでは、datetimeオブジェクトはisoformat文字列としてダンプされます。手動でstrftimeハッキングする必要はありません。
JRK

9
@jrk- datetimeオブジェクトからisoformat文字列への自動変換を取得していません。私にとって、simplejson.dumps(datetime.now())利回りTypeError: datetime.datetime(...) is not JSON serializable
コストモ

6
json.dumps(datetime.datetime.now().isoformat())魔法が起こる場所です。
ジェサニズム

2
simplejsonの優れている点は、複雑なデータ構造がある場合、それを解析してJSONに変換することです。すべての日時オブジェクトに対してjson.dumps(datetime.datetime.now()。isoformat())を実行する必要がある場合、それを失います。これを修正する方法はありますか?
andrewrk 2010

1
superjoe30:その方法については、stackoverflow.com / questions / 455580 /…を参照してください
最大

67

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

たとえば、datetime.now()で作成されたPythonのdatetimeオブジェクトdがあるとします。その値は次のとおりです。

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

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

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

サンプルの日時オブジェクトは次のようにシリアル化されます。

'"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オブジェクトに戻すことができます。

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)

50

を使用すると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"]'

7
マイナーな機能強化-使用obj.isoformat()dumps()他の便利な引数(などindent)を取る、より一般的な呼び出しを使用することもできます:simplejson.dumps(myobj、cls = JSONEncoder、...)
rcoup

3
それは、DateTimeJSONEncoderの親のメソッドではなく、JSONEncoderの親のメソッドを呼び出すためです。IE、あなたは2つのレベルを上げるでしょう。
Brian Arsuaga

30

標準ライブラリjsonモジュールを使用して、datetime.datetimeおよびdatetime.dateオブジェクトを再帰的にエンコードおよびデコードするためのかなり完全なソリューションを次に示します。%fdatetime.datetime.strptime()フォーマット文字列のフォーマットコードはそれ以降のみサポートされているため、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 __name__ == '__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))

このようdatetime.datetime.utcnow().isoformat()[:-3]+"Z"に日付を出力すると、JSON.stringify()がJavaScriptで生成するものとまったく同じになります
w00t

24

JavaScriptだけがJSONを消費することが確実な場合は、JavaScript Dateオブジェクトを直接渡すことをお勧めします。

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

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

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


12
技術的には無効なJSONですが、これは有効なJavaScriptオブジェクトリテラルです。(原則として、Content-Typeをapplication / jsonではなくtext / javascriptに設定します。)コンシューマーが常にそして永久にJavaScript実装のみである場合、これは非常にエレガントです。私はそれを使います。
システムの一時停止

13
.ctime()時間情報を渡す.isoformat()非常に悪い方法ですが、はるかに優れています。何を.ctime()行うことは、彼らが存在しないように、タイムゾーンと夏時間を捨てています。その機能は殺されるべきです。
エフゲニー

数年後:これを行うことを考慮しないでください。これは、JavaScriptでjsonをeval()する場合にのみ機能しますが、実際にはすべきではありません...
domenukk

11

ゲームの後半... :)

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

import json
import datetime

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

これで、json.dumps()を使用して、あたかもそれが常に日時をサポートしていたかのように...

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

これは、jsonモジュールへのこの拡張が常に必要であり、あなたや他の人がjsonシリアル化を使用する方法(既存のコードであるかどうかにかかわらず)を変更しないことを望む場合に意味があります。

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


6
これは、シリアライズ不可能なオブジェクトを黙って食べ、それらをに変えますNone。代わりに例外をスローすることをお勧めします。
Blender

6

タイムスタンプを除いて、コミュニティ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"

4

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の結果です



0

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

「ナイーブ」な時間(Pythonがタイムゾーンのない時間を呼び出し、これがローカルであると想定するもの)の場合、以下はローカルタイムゾーン強制し、UTCに正しく変換できるようにします。

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

PythonからJavaScriptへの日付変換では、日付オブジェクトは特定のISO形式、つまりISO形式またはUNIX番号である必要があります。ISO形式に情報が不足している場合は、最初にDate.parseを使用してUnix番号に変換できます。さらに、Date.parseはReactでも機能しますが、新しいDateは例外をトリガーする可能性があります。

ミリ秒のないDateTimeオブジェクトがある場合は、次のことを考慮する必要があります。:

  var unixDate = Date.parse('2016-01-08T19:00:00') 
  var desiredDate = new Date(unixDate).toLocaleDateString();

サンプルの日付は、API呼び出し後のresult.dataオブジェクト内の変数でも同じです。

日付を希望の形式で表示するオプション(長い平日を表示するなど)については、MDNドキュメントをご覧ください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.