lidatong / dataclasses-json

Easily serialize Data Classes to and from JSON
MIT License
1.34k stars 151 forks source link

[FEATURE] Overwrite encoder via to_dict? #451

Closed powellnorma closed 11 months ago

powellnorma commented 11 months ago

Description

It can be useful to supply a custom encoder function (e.g. for https://github.com/lidatong/dataclasses-json/issues/176#issuecomment-1292936712). Being able to do that via overwriting to_dict seems intuitive. However it looks like to_dict is not called for nested classes?

@dataclass
class A(DataClassJsonMixin):
    _x: int

    def to_dict(self, encode_json):
        r = super().to_dict(encode_json)
        r['x'] = r.pop('_x')
        return r

@dataclass
class B(DataClassJsonMixin):
    a: A

a = A(1)
b = B(a)
print(a.to_json())  # {"x": 1}
print(b.to_json())  # {"a": {"_x": 1}}

Possible solution

Check if object is a DataClassJsonMixin, and if so use to_dict while encoding

Alternatives

Context

No response

george-zubrienko commented 11 months ago

@powellnorma We'll start v1 API next week - please take a look at #442, it should solve your problem. Re implementing something in v0, I'm not sure its worth the time.

powellnorma commented 11 months ago

Hm ok, iiuc in v1 there will be no classes we can inherit for a to_dict method? So in v1 in order to overwrite the encoding behavior of a class, we would again do this?

dataclasses_json.cfg.global_config.encoders[DataPerson] = encode_data_person

Or will it be possible to define the encoding behavior in the class itself (without setting global_config.encoders explicitly?) I personally think that would make sense, e.g. with pickle one can define __setstate__ and __getstate__.

Also, will one only need to set

dataclasses_json.cfg.global_config.encoders[DataPerson] = encode_data_person

or also

dataclasses_json.cfg.global_config.encoders[list[DataPerson]] = ..

etc?

Thank you!

george-zubrienko commented 11 months ago

Basically you will need to define a serializer for the type in question and register it with smth like dcj.register(JsonSerializer[MyType]). Then if you have a = MyType(), you simply call to_dict(a) or to_json(a). There are more nuanced cases as well, noted in the issue. But in general, mutating user classes goes away in v1 and serDe behaviour becomes an addon you can modify any way you like by extending base serializer functionality.

powellnorma commented 11 months ago

Thank you. Would it make sense to let the base serializer check for a special method like to_dict or sdj_to_dict? Or perhaps could we let the user define a custom default serializer?

george-zubrienko commented 11 months ago

Or perhaps could we let the user define a custom default serializer?

This, most likely. I'll make sure to ping you on related PRs and readme updates when time comes :) Also thanks for raising the issue, it is important for us to understand the use cases that are problematic in v0.