web-dev-qa-db-ja.com

JSON.loadsでPython datetimeオブジェクトに変換する方法は?

JSONオブジェクトの文字列表現があります。

dumped_dict = '{"debug": false, "created_at": "2020-08-09T11:24:20"}'

このオブジェクトでjson.loadsを呼び出すと、

json.loads(dumped_dict)

私が得る;

{'created_at': '2020-08-09T11:24:20', 'debug': False}

ここには何も問題はありません。ただし、json.loadsを使用して上記のオブジェクトを次のようなものに変換する方法があるかどうかを知りたいです。

{'created_at': datetime.datetime(2020, 08, 09, 11, 24, 20), 'debug': False}

まもなく、json.loadsを呼び出すときに、datetime文字列を実際のdatetime.datetimeオブジェクトに変換できますか?

31
ozgur

これまでの私の解決策:

>>> json_string = '{"last_updated": {"$gte": "Thu, 1 Mar 2012 10:00:49 UTC"}}'
>>> dct = json.loads(json_string, object_hook=datetime_parser)
>>> dct
{u'last_updated': {u'$gte': datetime.datetime(2012, 3, 1, 10, 0, 49)}}


def datetime_parser(dct):
    for k, v in dct.items():
        if isinstance(v, basestring) and re.search("\ UTC", v):
            try:
                dct[k] = datetime.datetime.strptime(v, DATE_FORMAT)
            except:
                pass
    return dct

Object_hookの使用に関する詳細なリファレンス: JSONエンコーダーおよびデコーダー

私の場合、json文字列はGETリクエストからmy REST APIに送信されます。このソリューションを使用すると、クライアントとユーザーに__date__入力文字列がDATE_FORMATに準拠している限り、JSONに次のようになります。

DATE_FORMAT = '%a, %d %b %Y %H:%M:%S UTC'

正規表現パターンはおそらくさらに洗練されるべきです

PS:ご参考までに、json_stringはMongoDB/PyMongoクエリです。

21
Nicola Iarocci

object_hookを渡す必要があります。 ドキュメント から:

object_hookは、デコードされたオブジェクトリテラル(dict)の結果で呼び出されるオプションの関数です。 dictの代わりにobject_hookの戻り値が使用されます。

このような:

import datetime
import json

def date_hook(json_dict):
    for (key, value) in json_dict.items():
        try:
            json_dict[key] = datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S")
        except:
            pass
    return json_dict

dumped_dict = '{"debug": false, "created_at": "2020-08-09T11:24:20"}'
loaded_dict = json.loads(dumped_dict, object_hook=date_hook)

タイムゾーンも処理したい場合は、strptimeの代わりにdateutilを使用する必要があります。

17
galarant

私はNicolaと同じように2つの変更を提案します:

  1. dateutil.parserの代わりにdatetime.datetime.strptimeを使用してください
  2. キャッチする例外を明示的に定義します。通常は、空のexcept:を絶対に避けることをお勧めします

またはコードで:

import dateutil.parser

def datetime_parser(json_dict):
    for (key, value) in json_dict.items():
        try:
            json_dict[key] = dateutil.parser.parse(value)
        except (ValueError, AttributeError):
            pass
    return json_dict

str = "{...}"  # Some JSON with date
obj = json.loads(str, object_hook=datetime_parser)
print(obj)
3
Uri Shalit

あなたの質問が置かれる方法では、文字列が日付値であることをjsonに示すものはありません。これは、サンプルの文字列があるjsonのドキュメントとは異なります。

'{"__complex__": true, "real": 1, "imag": 2}'

この文字列にはインジケータ"__complex__": trueこれは、データのタイプを推測するために使用できますが、そのようなインジケーターがない限り、文字列は単なる文字列であり、すべての文字列を再正規化し、それらが日付のように見えるかどうかを判断するだけです。

あなたのケースでは、あなたのフォーマットにスキーマが利用可能であれば、間違いなくスキーマを使用すべきです。

3
Dov Grobgeld

次のように、正規表現を使用して、特定のフィールドを日時に変換するかどうかを決定できます。

def date_hook(json_dict):
    for (key, value) in json_dict.items():
        if type(value) is str and re.match('^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d*$', value):
            json_dict[key] = datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f")
        Elif type(value) is str and re.match('^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$', value):
            json_dict[key] = datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S")
        else:
            pass

    return json_dict

次に、json.loads()の呼び出しでobject_hookパラメーターを使用してdate_hook関数を参照できます。

json_data = '{"token": "faUIO/389KLDLA", "created_at": "2016-09-15T09:54:20.564"}'
data_dictionary = json.loads(json_data, object_hook=date_hook)
2
Maciej

私が知る限り、これにはすぐに使える解決策はありません。

最初に、文字列と日時を正しく区別するには、ソリューションで json schema を考慮する必要があります。ある程度、jsonスキーマ推論ツール(jsonスキーマ推論ツールgithubのグーグル)でスキーマを推測し、実際に日時である場所を修正できます。

スキーマがわかっている場合は、jsonを解析して文字列表現を日時に置き換える関数を作成するのはかなり簡単です。コードのいくつかのインスピレーションは、おそらく validictory productから見つけることができます(JSONスキーマの検証も良い考えです)。

1
Roman Susi

Nicolaの answer に触発されてpython3に適合(ベースストリングではなくstr):

import re
from datetime import datetime
datetime_format = "%Y-%m-%dT%H:%M:%S"
datetime_format_regex = re.compile(r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$')


def datetime_parser(dct):
    for k, v in dct.items():
        if isinstance(v, str) and datetime_format_regex.match(v):
            dct[k] = datetime.strptime(v, datetime_format)
    return dct

これにより、try/exceptメカニズムの使用が回避されます。 OPのテストコード:

>>> import json
>>> json_string = '{"debug": false, "created_at": "2020-08-09T11:24:20"}'
>>> json.loads(json_string, object_hook=datetime_parser)
{'created_at': datetime.datetime(2020, 8, 9, 11, 24, 20), 'debug': False}

正規表現とdatetime_format変数は、他のパターンに合わせて簡単に調整できます。真ん中にTなし。

Isoformatで保存された(したがってマイクロ秒で保存された)文字列をdatetimeオブジェクトに戻すには、 この質問 を参照してください。

0

このメソッドは、日時形式で再帰的な文字列検索を実装します

import json
from dateutil.parser import parse

def datetime_parser(value):
    if isinstance(value, dict):
        for k, v in value.items():
            value[k] = datetime_parser(v)
    Elif isinstance(value, list):
        for index, row in enumerate(value):
            value[index] = datetime_parser(row)
    Elif isinstance(value, str) and value:
        try:
            value = parse(value)
        except (ValueError, AttributeError):
            pass
    return value

json_to_dict = json.loads(YOUR_JSON_STRING, object_hook=datetime_parser)
0
Maksim Senchuk