-
Notifications
You must be signed in to change notification settings - Fork 13
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
Incompatibility of custom toJson/fromJson with other non-Celest code #38
Comments
Hey @marcglasberg, thanks for the detailed issue. It brings to light a lot of the challenges around designing a framework like Celest and where to draw the lines of simple and opinionated. Please bear with me as I walk through my thoughts on these topics below. Opinionated vs. CustomizableCelest aims to be an opinionated framework. What that means is that we aim for there to be a single way of doing things whenever possible, even at the cost of decreased customizability. Packages like
|
Yes, you are right. When you say It brings a larger point that I was afraid when I started creating the demo app: That using the same classes in the frontend/backend may be "overloading" as well. I was afraid that sharing classes like But I digress. When the question is "JSON to do what?" there is a Two final notes:
May I close this issue? |
Good points. I'd like to think on this some more before closing it. I would love to not have a situation where client and server need distinct types. |
Hey @marcglasberg, I've stumbled upon a pretty clean solution to this. Let me know what you think! I've added tests for all your types into my CLI suite to make sure I've covered all the cases. The solutions all revolve around a feature called "custom overrides" which is available in the latest dev release of Celest. Here's my spiel for the changelog:
Here is how I've done the overrides for your model types: import 'package:_common/marcelo.dart' as models;
@override
extension type AvailableStock(models.AvailableStock _)
implements models.AvailableStock {}
@override
extension type AvailableStocks(models.AvailableStocks _)
implements models.AvailableStocks {}
@override
extension type CashBalance(models.CashBalance _)
implements models.CashBalance {}
@override
extension type Portfolio(models.Portfolio _) implements models.Portfolio {}
@override
extension type Stock(models.Stock _) implements models.Stock {}
@override
extension type Ui(models.Ui _) implements models.Ui {} For the exception types, things get a bit more interesting, because many of them are not naturally serializable. For example, many have fields of type @override
extension type AppError(core.AppError _err) implements core.AppError {
AppError.fromJson(Map<String, Object?> json)
: _err = core.AppError(json['msg'], json['error']);
Map<String, Object?> toJson() => {'msg': _err.message, 'error': _err.error};
}
@override
extension type AppException(core.AppException _ex)
implements core.AppException {
JsonValue? get error => _ex.error as JsonValue?;
JsonValue? get msg => _ex.msg as JsonValue?;
} If a In this case, you can either write your own @override
extension type UserException._(core.UserException _ex)
implements core.UserException {
UserException({
String? msg,
JsonValue? cause,
}) : this._(core.UserException(msg, cause: cause));
Null get code => null;
JsonValue? get cause => _ex.cause as JsonValue?;
} In this case, I chose to redeclare the |
Also note that these override types are only ever used by Celest when serializing. For example, defining an override on |
This change has been released in 0.2.0 🚀 Let me know if it's a good solution for your issue! Happy to spend more time exploring alternatives. |
@dnys1 Yes, it's a really good solution, I love it! Congrats! One minor thing is, why are you using |
Yeah, that could definitely be true. I thought I like Thanks for bringing it up and I'm glad you are enjoying the feature! 🥳 I will close this issue for now, but will follow up with you on the |
Celest allows me to define custom serialization/deserialization by implementing
toJson/fromJson
in a model class.That's fine until you have some other needs for these serializer methods. For example, in here I need to save the app state to the local device disk. To that end, I also use
toJson/fromJson
.For example, here I have:
And this
toJson/fromJson
code is then used in a "local disk persistor" hereBut now, instead of creating its own serializers, Celest will use the
toJson/fromJson
I've created. That's a waste, as my goal here was not to customize the serialization, but only to provide my persistor with validtoJson/fromJson
methods. Ideally, Celest should have written those serializer methods for me, and I must find a way to use them in my persistor too.So, I'll try using Celest's serialization:
This doesn't work, because Celest will pick up the
toJson/fromJson
methods and will do this:This will result in a StackOverflow!
Here is a simple solution: instead of calling these methods
toJson
andfromJson
, I can call themtoJsonX
andfromJsonX
:Now Celest will correctly generate its default serializers, which I am using. And now I can use
toJsonX
andfromJsonX
in my persistor:This works, but I'd like to be able to name these methods
toJson
andfromJson
instead oftoJsonX
andfromJsonX
. Also note that I have control over which methods my persistor uses, but that's not always the case. Some packages in the wild (like JsonSerializable) only work withtoJson
andfromJson
and it would be nice for Celest's serialization to be compatible.I propose one of these solutions:
toJson/fromJson
if we add an annotation to them, like@CustomSerializer
; OrtoJson/fromJson
if we add an annotation to them, like@CelestIgnore
; OrtoJson/fromJson
methods are using a CelestSerializer
subclass internally, it ignores them automatically.The text was updated successfully, but these errors were encountered: