Make the Python json encoder support Python's new dataclasses

前端 未结 6 1253
忘掉有多难
忘掉有多难 2021-02-03 16:59

Starting with Python 3.7, there is something called a dataclass:

from dataclasses import dataclass

@dataclass
class Foo:
    x: str

However, t

相关标签:
6条回答
  • 2021-02-03 17:37

    If you are ok with using a library for that, you can use dataclasses-json. Here is an example:

    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()
    

    It also supports embedded dataclasses - if your dataclass has a field typed as another dataclass - if all dataclasses envolved have the @dataclass_json decorator.

    0 讨论(0)
  • 2021-02-03 17:38

    I'd suggest creating a parent class for your dataclasses with a to_json() method:

    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}'
    

    This is especially useful if you have other functionality to add to all your dataclasses.

    0 讨论(0)
  • 2021-02-03 17:40

    Can't you just use the dataclasses.asdict() function to convert the dataclass to a dict? Something like:

    >>> @dataclass
    ... class Foo:
    ...     a: int
    ...     b: int
    ...     
    >>> x = Foo(1,2)
    >>> json.dumps(dataclasses.asdict(x))
    '{"a": 1, "b": 2}'
    
    0 讨论(0)
  • 2021-02-03 17:44

    A much simpler answer can be found on Reddit using dictionary unpacking

    >>> 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)
    
    0 讨论(0)
  • 2021-02-03 17:50

    Ways of getting JSONified dataclass instance

    There are couple of options to accomplish that goal, selection of each imply analyze on which approach suits best for your needs:

    Standart library: 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"}'
    

    Picking it back to dataclass instance isn't trivial, so you may want to visit that answer https://stackoverflow.com/a/53498623/2067976

    Marshmallow Dataclass

    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)
    

    As a bonus for marshmallow_dataclass you may use validation on the field itself, that validation will be used when someone deserialize the object from json using that schema.

    Dataclasses 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')
    

    Also, in addition to that notice that marshmallow dataclass did type conversion for you whereas dataclassses-json(ver.: 0.5.1) ignores that.

    Write Custom Encoder

    Follow accepted miracle2k answer and reuse custom json encoder.

    0 讨论(0)
  • 2021-02-03 17:53

    Much like you can add support to the JSON encoder for datetime objects or Decimals, you can also provide a custom encoder subclass to serialize dataclasses:

    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)
    
    0 讨论(0)
提交回复
热议问题