programing

날짜 시간을 지원하는 파이썬 JSON 인코더?

yellowcard 2023. 11. 1. 22:15
반응형

날짜 시간을 지원하는 파이썬 JSON 인코더?

Python JSON 인코더가 date time을 지원하도록 만드는 우아한 방법이 있습니까?서드파티 모듈인가요 아니면 쉬운 해킹인가요?

저는 json을 생성하기 위해 db에서 몇 개의 행을 가져오기 위해 토네이도의 데이터베이스 포장지를 사용하고 있습니다.쿼리 결과에는 정규 MySQL 타임스탬프 열이 포함됩니다.

Python의 기본 json 인코더가 모든 종류의 데이터베이스 쿼리에서 매우 일반적인 자체 datetime 유형을 지원하지 않는다는 것은 매우 짜증나는 일입니다.

나는 파이썬의 json 인코더를 수정하고 싶지 않습니다.좋은 연습이 있습니까?정말 고마워.

ps: 파이썬 JSON 인코더 기본 메서드를 수정하여 더러운 해킹을 찾았습니다.

변경 사항:

def default(self, o):
    raise TypeError(repr(o) + " is not JSON serializable")

받는 사람:

def default(self, o):
    from datetime import date
    from datetime import datetime
    if isinstance(o, datetime):
        return o.isoformat()
    elif isinstance(o, date):
        return o.isoformat()
    else:
        raise TypeError(repr(o) + " is not JSON serializable")

그것은 개발 환경만을 위한 일시적인 해결책이 될 것입니다.

하지만 장기적인 솔루션이나 운영 환경의 경우, 이는 매우 부적절하기 때문에 새 서버에 구축할 때마다 수정 작업을 수행해야 합니다.

더 좋은 방법이 있습니까?나는 파이썬 코드 자체를 수정하고 싶지 않고, 토네이도 소스 코드도 수정하고 싶지 않습니다.제가 프로젝트 코드를 사용해서 이를 실현할 수 있는 방법이 있을까요? 가급적이면 한 번에 가능합니다.

정말 고마워.

json.dumps(thing, default=str)

문서에서는 JSON 인코더를 서브클래싱하고 자신만의 기본 메서드를 구현할 것을 제안합니다.당신은 기본적으로 거기에 있는 것 같군요.

기본 인코더에서 날짜를 처리하지 않는 이유는 JSON에 날짜의 표준 표현이 없기 때문입니다.일부 사용자는 형식을 사용합니다./Date(1198908717056)/, 하지만 저는 개인적으로 ISO 형식을 선호합니다.

import json
import datetime


class DateTimeEncoder(json.JSONEncoder):

    def default(self, obj):
        if isinstance(obj, (datetime.datetime, datetime.date, datetime.time)):
            return obj.isoformat()
        elif isinstance(obj, datetime.timedelta):
            return (datetime.datetime.min + obj).time().isoformat()

        return super(DateTimeEncoder, self).default(obj)

now = datetime.datetime.now()
encoder = DateTimeEncoder()
encoder.encode({"datetime": now, "date": now.date(), "time": now.time()})
> {"datetime": "2019-07-02T16:17:09.990126", "date": "2019-07-02", "time": "16:17:09.990126"}

프로젝트를 위해 직접 수업을 만들었습니다.

import datetime
import decimal
import json
import sys

class EnhancedJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            ARGS = ('year', 'month', 'day', 'hour', 'minute',
                     'second', 'microsecond')
            return {'__type__': 'datetime.datetime',
                    'args': [getattr(obj, a) for a in ARGS]}
        elif isinstance(obj, datetime.date):
            ARGS = ('year', 'month', 'day')
            return {'__type__': 'datetime.date',
                    'args': [getattr(obj, a) for a in ARGS]}
        elif isinstance(obj, datetime.time):
            ARGS = ('hour', 'minute', 'second', 'microsecond')
            return {'__type__': 'datetime.time',
                    'args': [getattr(obj, a) for a in ARGS]}
        elif isinstance(obj, datetime.timedelta):
            ARGS = ('days', 'seconds', 'microseconds')
            return {'__type__': 'datetime.timedelta',
                    'args': [getattr(obj, a) for a in ARGS]}
        elif isinstance(obj, decimal.Decimal):
            return {'__type__': 'decimal.Decimal',
                    'args': [str(obj),]}
        else:
            return super().default(obj)


class EnhancedJSONDecoder(json.JSONDecoder):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, object_hook=self.object_hook,
                         **kwargs)

    def object_hook(self, d): 
        if '__type__' not in d:
            return d
        o = sys.modules[__name__]
        for e in d['__type__'].split('.'):
            o = getattr(o, e)
        args, kwargs = d.get('args', ()), d.get('kwargs', {})
        return o(*args, **kwargs)

if __name__ == '__main__':
    j1 = json.dumps({'now': datetime.datetime.now(),
        'val': decimal.Decimal('9.3456789098765434987654567')},
        cls=EnhancedJSONEncoder)
    print(j1)
    o1 = json.loads(j1, cls=EnhancedJSONDecoder)
    print(o1)

결과:

{"val": {"args": ["9.3456789098765434987654567"], "__type__": "decimal.Decimal"}, "now": {"args": [2014, 4, 29, 11, 44, 57, 971600], "__type__": "datetime.datetime"}}
{'val': Decimal('9.3456789098765434987654567'), 'now': datetime.datetime(2014, 4, 29, 11, 44, 57, 971600)}

참조:

참고: 키와 arg, kwarg를 값으로 하는 사용자 정의 사전을 인코더의 값으로 전달함으로써 보다 유연하게 만들 수 있습니다.__init__()(또는 기본 사전) 을 사용합니다.default()방법.

json.dumps(r, default=lambda o: o.isoformat() if hasattr(o, 'isoformat') else o)

사용자 지정 디코더/인코더 만들기:

class CustomJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            return http_date(obj)
        if isinstance(obj, uuid.UUID):
            return str(obj)
        return json.JSONEncoder.default(self, obj)

class CustomJSONDecoder(json.JSONDecoder):
    def __init__(self, *args, **kwargs):
        json.JSONDecoder.__init__(self, object_hook=self.object_hook, *args, **kwargs)

    def object_hook(self, source):
        for k, v in source.items():
            if isinstance(v, str):
                try:
                    source[k] = datetime.datetime.strptime(str(v), '%a, %d %b %Y %H:%M:%S %Z')
                except:
                    pass
        return source

Tryton 프로젝트에는 다음을 위한 JSON 인코더 구현이 있습니다.datetime.datetime,datetime.date그리고.datetime.time물건을 (다른 사람과) 같이서버와 클라이언트 간의 JSON RPC 통신에 사용됩니다.

http://hg.tryton.org/2.4/trytond/file/ade5432ac476/trytond/protocols/jsonrpc.py#l53 참조

datetime 유형을 unix 타임스탬프로 변환한 다음 내용을 json으로 인코딩합니다.

예: http://codepad.org/k3qF09Kr

저는 그 소포나 그 소포를 사용하는 것을 추천합니다.

훨씬 더 빠르며 여전히 여러 가지 복잡한 유형을 지원합니다.

사용자 지정 인코더 만들기

(콜의 답변에 작지만 중요한 추가 사항은 pd의 처리입니다.NaT(또는 null/빈 타임스탬프 값). 추가하지 않으면 NaT/누락된 타임스탬프 데이터에 대해 매우 이상한 타임스탬프 변환이 발생하기 때문입니다.)

class CustomEncoder(json.JSONEncoder):
    def default(self, obj):
        if pd.isnull(obj):
            return None
        elif isinstance(obj, datetime):
            return obj.isoformat()
        elif isinstance(obj, date):
            return obj.isoformat()
        elif isinstance(obj, timedelta):
            return (datetime.min + obj).time().isoformat()
        else:
            return super(CustomEncoder, self).default(obj)

그런 다음 이를 사용하여 데이터 프레임을 인코딩합니다.

df_as_dict = df.to_dict(outtype = 'records')  # transform to dict

df_as_json = CustomEncoder().encode(df_as_dict) #transform to json

인코더가 데이터를 표준화했기 때문에, 일반 디코더는 데이터를 다시 데이터 프레임으로 변환하는 데 있어 잘 작동할 것입니다.

result_as_dict = json.JSONDecoder().decode(df_as_json) # decode back to dict

result_df = pd.DataFrame(result)  # transform dict back to dataframe

물론 인코딩하기 전에 데이터 프레임을 더 큰 dict에 입력하는 경우에도 이러한 작업이 가능합니다.

input_dict = {'key_1':val_1,'key_2':val_2,...,'df_as_dict':df_as_dict}
input_json = CustomEncoder().encode(input_dict)
input_json_back_as_dict = json.JSONDecoder().decode(input_json)
input_df_back_as_dict = input_json_back_as_dict['df_as_dict']
input_df_back_as_df = pd.DataFrame(input_df_back_as_dict)

언급URL : https://stackoverflow.com/questions/12122007/python-json-encoder-to-support-datetime

반응형