-
Notifications
You must be signed in to change notification settings - Fork 156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Why is a class decorator or inheritance approach required? #243
Comments
Totally agree. I don't want my dataclasses to have >>> person = Person(name='Hans', age=26)
>>> library.to_dict(person)
{'name': 'Hans', 'age': 26} What reason is there to make the interface any more complicated than that? If I have 15 dataclasses in my project, why do I have to decorate all 15 of them with |
Hi @alanfranz I'm sorry for not responding. The answer is you can achieve what you want with the library as is and plain Python: DataClassJsonMixin.to_json(MyClass, my_class_instance)
DataClassJsonMixin.from_json(MyClass, my_class_instance) You can alias those methods in your own code as in: to_json = DataClassJsonMixin.to_json if the above is too verbose |
@lidatong uhmm, I don't see how that can work. Maybe in Python2? There's no "unbound method" concept in Python3, and it wouldn't work anyway (where is a plain dataclass to find the to_dict() method which is required in to_json()?) See the following example, tested with Python 3.9.2.
Error if 1st line uncommented:
Error if 2nd line uncommented:
I apologize if I misunderstood anything. |
@lidatong I suppose you might have wanted to say something like this:
Which seems to work. But it relies on an implementation detail - it seems that there's no postprocessing of the decorated class in this example. But the dataclass_json decorator DOES perform a postprocessing in certain cases (see dataclasses-json/dataclasses_json/api.py Line 138 in 3dc59e0
I think the serialization logic could be extracted, then called from the mixin or decorator. If you think it can be useful and merged, I'm willing to write a PR. |
@alanfranz sorry my mistake, when I responded I didn't have my laptop with me and was going off memory I meant to suggest your 2nd example I forgot internally I'm ok with making that change so you can do
I'm not sure what you mean by "unbound method"... but a method is just a function that is implicitly passed the instance class SomeMixin:
def do_something(self):
print("doing something")
class A:
pass
a = A()
SomeMixin.do_something(a) |
Please don't go down the If you want to support this feature, |
@lidatong now I feel old :-) an unbound method is a Python2 thing; when a function is passed in a class body, it becomes an unbound method, which means that it's not "just a function" anymore;
Would raise an UnboundMethodException or something like that. You could, BTW, pass an instance of the correct class in order to call such method - it was quite unuseful, btw. When called on an instance, it would become a "bound method", which would mean that the instance is passed as the first argument. It works the same in Python3, but the concept of "unbound method" was removed, so the function can be called on the class as if it were static. By the way, for both: I think the approach is jackson-like, we need to separate the mapper from the object hierarchy. My idea would be something like that:
So anyone is free to configure one, two, or thirty mapper as they see fit. Of course there would be a defaultMapper instance which would to default things. The current mixins and functions could stay the same, but would configure/call a mapper instead of operating directly. @lidatong let me know what you think of this approach. |
I agree the logic that converts instances to dicts/json should be separated from the class hierarchy. I don't really care how exactly that's implemented (I'll be happy as long as the amount of boilerplate code doesn't grow linearly with the number of dataclasses in my code base), but I figure I might as well share my thoughts regarding the design. IMO, a
It's trivial and intuitive to customize this process by passing the relevant arguments into |
pardon me @Aran-Fey but I disagree. There usually is some configuration regarding serialization (I'm not sure about this specific library, I'm taking a general POV), which can differ from a Python dictionary (e.g. do we serialize None as null or we just don't serialize the key? Do we write integers as numbers or strings? Do we map names somehow - e.g. camelCase vs under_score) currently, such configuration is stored (as far as I can understand) in the decorated class and/or instance. As soon as we drop that link, we need a place to store such configuration. Passing a configuration dictionary every time to free functions is tedious and error-prone, isn't it? What problem do you see with Mapper? Take a look at Jackson's own mapper - https://fasterxml.github.io/jackson-databind/javadoc/2.7/com/fasterxml/jackson/databind/ObjectMapper.html - it's got exactly zero required configuration. If want and need, you can configure it differently.
would be so difficult? Of course there can be a global, default Mapper instance and a default static method to_dict/to_json, maybe with configuration overrides. But I think it's quite unlikely that you'd have to change config options at each call; you'll probably be configuring one or two mappers and use them consistenly throughout your app. |
I don't think comparing Java libs with Python libs is a way to get stuff done. In Scala case classes are ser-desered with a single implicit import. Doesn't mean we should implement Scala type system. |
Hello,
first things first: thanks for this project! I had a similar idea many years ago, but without type annotations it failed miserably.
I have a question: why are we absolutely required to change the class that we want to map to/from json by using a decorator or inheritance?
Counter-example: in Java, when using things like jackson ( https://github.com/FasterXML/jackson - example usage https://www.stubbornjava.com/posts/practical-jackson-objectmapper-configuration ) we can define a mapper and use it to convert plain Java objects to JSON and vice-versa. We don't need to do anything with our Java object unless we need to do something special (and, even there, Jackson and similar tools usually allow to configure a mapper, even though it's a bit harder than using an annotation).
This makes it easy to work with third-party libraries where you don't control the class you're returned, and/or you don't need to change your dataclasses all around the code. Also, if you need different serialization options in various parts of your library, you can just define multiple mappers and call it a day (imagine I need to serialize my objects to json, xml, and protobuf, I'd need to decorate my dataclass three times instead of defining three separate mappers).
I took a peek at the code and I couldn't answer; do you think there's something really binding for the current implementation approach?
The text was updated successfully, but these errors were encountered: