programing

Python json 인코더가 Python의 새 데이터 클래스를 지원하도록 합니다.

subpage 2023. 6. 13. 22:18
반응형

Python json 인코더가 Python의 새 데이터 클래스를 지원하도록 합니다.

Python 3.7부터는 데이터 클래스라는 것이 있습니다.

from dataclasses import dataclass

@dataclass
class Foo:
    x: str

그러나 다음은 실패합니다.

>>> import json
>>> foo = Foo(x="bar")
>>> json.dumps(foo)
TypeError: Object of type Foo is not JSON serializable

어떻게 만들 수 ?json.dumps()의인스인의 합니다.Foojson 객체로?

JSON 인코더에 객체 또는 소수점에 대한 지원을 추가할 수 있는 것처럼 사용자 지정 인코더 하위 클래스를 제공하여 데이터 클래스를 직렬화할 수도 있습니다.

import dataclasses, json

class EnhancedJSONEncoder(json.JSONEncoder):
        def default(self, o):
            if dataclasses.is_dataclass(o):
                return dataclasses.asdict(o)
            return super().default(o)

json.dumps(foo, cls=EnhancedJSONEncoder)

당신은 그냥 사용할 수 없습니까?dataclasses.asdict()데이터 클래스를 딕트로 변환하는 기능?다음과 같은 것:

>>> @dataclass
... class Foo:
...     a: int
...     b: int
...     
>>> x = Foo(1,2)
>>> json.dumps(dataclasses.asdict(x))
'{"a": 1, "b": 2}'

JSONified 데이터 클래스 인스턴스를 가져오는 방법

이러한 목표를 달성하기 위한 몇 가지 옵션이 있습니다. 각 옵션을 선택하면 고객의 요구에 가장 적합한 접근 방식을 분석할 수 있습니다.

표준 라이브러리: dataclass.asdict

import dataclasses
import json


@dataclass.dataclass
class Foo:
    x: str

foo = Foo(x='1')
json_foo = json.dumps(dataclasses.asdict(foo)) # '{"x": "1"}'

데이터 클래스 인스턴스로 다시 선택하는 것은 사소한 일이 아니므로 해당 답변을 방문하는 것이 좋습니다. https://stackoverflow.com/a/53498623/2067976

마시멜로 데이터 클래스

from dataclasses import field
from marshmallow_dataclass import dataclass


@dataclass
class Foo:
    x: int = field(metadata={"required": True})

foo = Foo(x='1') # Foo(x='1')
json_foo = foo.Schema().dumps(foo) # '{"x": "1"}'

# Back to class instance.
Foo.Schema().loads(json_foo) # Foo(x=1)

보로스너의 로.marshmallow_dataclass필드 자체에 대해 유효성 검사를 사용할 수 있습니다. 누군가가 해당 스키마를 사용하여 json의 개체를 역직렬화할 때 유효성 검사가 사용됩니다.

데이터 클래스 Json

from dataclasses import dataclass
from dataclasses_json import dataclass_json


@dataclass_json
@dataclass
class Foo:
    x: int

foo = Foo(x='1')
json_foo = foo.to_json() # Foo(x='1')
# Back to class instance
Foo.from_json(json_foo) # Foo(x='1')

또한 마시멜로 데이터 클래스가 유형 변환을 수행했다는 사실도 알 수 있습니다. 반면 data classes-json(ver.: 0.5.1)은 이를 무시합니다.

쓰기 사용자 지정 인코더

승인된 Miracle2k 답변을 따르고 사용자 지정 json 인코더를 재사용합니다.

라이브러리를 사용해도 괜찮다면 dataclass-json을 사용할 수 있습니다.다음은 예입니다.

from dataclasses import dataclass

from dataclasses_json import dataclass_json


@dataclass_json
@dataclass
class Foo:
    x: str


foo = Foo(x="some-string")
foo_json = foo.to_json()

또한 데이터 클래스에 다른 데이터 클래스로 입력된 필드가 있는 경우 포함된 데이터 클래스를 지원합니다. 관련된 모든 데이터 클래스가@dataclass_json장식가

또다을구수있다니습할현을 할 수도 .asdict그리고.json.dumps클래스 내의 메서드입니다.이 경우에는 가져올 필요가 없습니다.json.dumps프로젝트의 다른 부분으로:


from typing import List
from dataclasses import dataclass, asdict, field
from json import dumps


@dataclass
class TestDataClass:
    """
    Data Class for TestDataClass
    """
    id: int
    name: str
    tested: bool = False
    test_list: List[str] = field(default_factory=list)

    @property
    def __dict__(self):
        """
        get a python dictionary
        """
        return asdict(self)

    @property
    def json(self):
        """
        get the json formated string
        """
        return dumps(self.__dict__)


test_object_1 = TestDataClass(id=1, name="Hi")
print(test_object_1.__dict__)
print(test_object_1.json)

출력:

{'id': 1, 'name': 'Hi', 'tested': False, 'test_list': []}
{"id": 1, "name": "Hi", "tested": false, "test_list": []}

메서드를 상속할 상위 클래스를 만들 수도 있습니다.

from typing import List
from dataclasses import dataclass, asdict, field
from json import dumps


@dataclass
class SuperTestDataClass:

    @property
    def __dict__(self):
        """
        get a python dictionary
        """
        return asdict(self)

    @property
    def json(self):
        """
        get the json formated string
        """
        return dumps(self.__dict__)


@dataclass
class TestDataClass(SuperTestDataClass):
    """
    Data Class for TestDataClass
    """
    id: int
    name: str
    tested: bool = False
    test_list: List[str] = field(default_factory=list)


test_object_1 = TestDataClass(id=1, name="Hi")
print(test_object_1.__dict__)
print(test_object_1.json)


에 부모 를 만들 것을 to_json()방법:

import json
from dataclasses import dataclass, asdict

@dataclass
class Dataclass:
    def to_json(self) -> str:
        return json.dumps(asdict(self))

@dataclass
class YourDataclass(Dataclass):
    a: int
    b: int

x = YourDataclass(a=1, b=2)
x.to_json()  # '{"a": 1, "b": 2}'

이 기능은 모든 데이터 클래스에 추가할 다른 기능이 있는 경우 특히 유용합니다.

가장간인방법딩코한단방을 하는 가장 dataclass그리고.SimpleNamespace는 기본 을 객는기기제니다합공능에 제공하는 입니다.json.dumps()수 합니다.__dict__:

json.dumps(foo, default=lambda o: o.__dict__)

데이터 클래스 마법사는 사용자에게 유용한 최신 옵션입니다.날짜 및 시간과 같은 복잡한 유형을 지원합니다. 대부분의 일반 데이터는typing모듈 및 중첩된 데이터 클래스 구조입니다.

PEP 585604에 도입된 "새로운 스타일" 주석은 다음을 통해 Python 3.7로 포팅할 수 있습니다.__future__아래와 같이 수입합니다.

from __future__ import annotations  # This can be removed in Python 3.10
from dataclasses import dataclass, field
from dataclass_wizard import JSONWizard


@dataclass
class MyClass(JSONWizard):
    my_str: str | None
    is_active_tuple: tuple[bool, ...]
    list_of_int: list[int] = field(default_factory=list)


string = """
{
  "my_str": 20,
  "ListOfInt": ["1", "2", 3],
  "isActiveTuple": ["true", false, 1]
}
"""

instance = MyClass.from_json(string)
print(repr(instance))
# MyClass(my_str='20', is_active_tuple=(True, False, True), list_of_int=[1, 2, 3])

print(instance.to_json())
# '{"myStr": "20", "isActiveTuple": [true, false, true], "listOfInt": [1, 2, 3]}'

# True
assert instance == MyClass.from_json(instance.to_json())

데이터 클래스 마법사를 설치할 수 있습니다.pip:

$ pip install dataclass-wizard

약간의 배경 정보:

직렬화의 경우 약간 수정된 (조금 더 효율적인) 구현을 사용합니다. JSON을 데이터 클래스 인스턴스로 직렬화 해제할 때 JSON은 데이터 클래스 필드를 통해 처음 반복되고 주석이 달린 각 유형에 대한 파서를 생성하므로 직렬화 해제 프로세스가 여러 번 실행될 때 더 효율적입니다.

고지 사항:저는 이 라이브러리의 작성자(및 유지관리자)입니다.

익살스러운

pydantic 모델을 사용하면 데이터 클래스와 유사한 환경을 경험할 수 있고 딕트 및 Json 변환을 완벽하게 지원할 수 있습니다.

Python 3.9 이상:

from typing import Optional
from pydantic import BaseModel, parse_obj_as, parse_raw_as


class Foo(BaseModel):
    count: int
    size: Optional[float] = None


f1 = Foo(count=10)

# Parse to dict
print(f1.dict())
# OUT: {'count': 10, 'size': None}

# Load from dict
f2 = Foo.parse_obj({"count": 20})

# Parse to json
print(f2.json())
# OUT: {"count": 20, "size": null}

추가 옵션:

# Load from json string
f3 = Foo.parse_raw('{"count": 30}')

# Load from json file
f4 = Foo.parse_file("path/to/data.json")

# Load from list of dicts
f_list1 = parse_obj_as(list[Foo], [{"count": 110}, {"count": 120}])
print(f_list1)
# OUT: [Foo(count=110, size=None), Foo(count=120, size=None)]

# Load from list in json string
f_list2 = parse_raw_as(list[Foo], '[{"count": 130}, {"count": 140}]')
print(f_list2)
# OUT: [Foo(count=130, size=None), Foo(count=140, size=None)]

복잡한 계층형 데이터 구조

class Bar(BaseModel):
    apple = "x"
    banana = "y"


class Spam(BaseModel):
    foo: Foo
    bars: list[Bar]


m = Spam(foo={"count": 4}, bars=[{"apple": "x1"}, {"apple": "x2"}])
print(m)
# OUT: foo=Foo(count=4, size=None) bars=[Bar(apple='x1', banana='y'), Bar(apple='x2', banana='y')]

print(m.dict())
# OUT:
# {
#     'foo': {'count': 4, 'size': None},
#     'bars': [
#         {'apple': 'x1', 'banana': 'y'},
#         {'apple': 'x2', 'banana': 'y'},
#     ],
# }

Pydantic은 다양한 표준 유형을 지원합니다(예:datetime) 및 일반적으로 사용되는 특수 유형(예:EmailStr그리고.HttpUrl):

from datetime import datetime
from pydantic import HttpUrl


class User(BaseModel):
    name = "John Doe"
    signup_ts: datetime = None
    url: HttpUrl = None


u1 = User(signup_ts="2017-07-14 00:00:00")
print(u1)
# OUT: signup_ts=datetime.datetime(2017, 7, 14, 0, 0) url=None name='John Doe'

u2 = User(url="http://example.com")
print(u2)
# OUT: signup_ts=None url=HttpUrl('http://example.com', ) name='John Doe'

u3 = User(url="ht://example.com")
# OUT:
#  ValidationError: 1 validation error for User
#  url
#    URL scheme not permitted (type=value_error.url.scheme; allowed_schemes={'http', 'https'})

정말로 사용해야 하는 경우json.dumps사용자 지정 인코더 작성:

import json


class EnhancedJSONEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, BaseModel):
            return o.dict()
        return super().default(o)


foo = Foo(count=20)
json.dumps([{"foo": foo}], cls=EnhancedJSONEncoder)
# OUT: '[{"foo": {"count": 20, "size": null}}]'

json 형성 방법을 제공하는 데이터 클래스

import json
from dataclasses import dataclass

@dataclass
class Foo:
    x: str
   
    def to_json(self):
      return json.dumps(self.__dict__)

Foo("bar").to_json()
>>> '{"x":"bar"}'

사전 압축 풀기를 사용하여 훨씬 더 간단한 답을 Reddit에서 찾을 수 있습니다.

>>> from dataclasses import dataclass
>>> @dataclass
... class MyData:
...   prop1: int
...   prop2: str
...   prop3: int
...
>>> d = {'prop1': 5, 'prop2': 'hi', 'prop3': 100}
>>> my_data = MyData(**d)
>>> my_data
MyData(prop1=5, prop2='hi', prop3=100)

자, 여기 제가 비슷한 상황에 처했을 때 한 일이 있습니다.

  1. 중첩된 데이터 클래스를 사전으로 변환하는 사용자 정의 사전 팩토리를 만듭니다.

    def myfactory(data): dict 반환(x[1]이 None이 아닌 경우 x는 데이터의 x에 해당)

  2. foo가 @data 클래스인 경우 사전 팩토리에서 "myfactory()" 메서드를 사용하도록 제공하기만 하면 됩니다.

    fooDict = asdict(foo, dict_factory=myfactory)

  3. fooDict를 json으로 변환

    fooJson = json.dump(fooDict)

이것은 효과가 있을 것입니다!!

언급URL : https://stackoverflow.com/questions/51286748/make-the-python-json-encoder-support-pythons-new-dataclasses

반응형