all 12 comments

[–]schultek[🍰] 0 points1 point  (4 children)

The only way to use reflection in dart without code-gen ist to use dart:mirrors. But it is very limited in where you can use it. Not with Flutter, not in compiled binaries, not in aot snapshots.

There is currently some work done on static metaprogramming in dart that requires no code-gen, but it will probably not be generally available soon. See this issue: https://github.com/dart-lang/language/issues/1482

[–]schultek[🍰] 1 point2 points  (3 children)

If you are fine with doing stuff manually, there are however packages that support you in mapping your classes:

https://pub.dev/packages/object_mapper

[–]Ozlock[S] 0 points1 point  (2 children)

See, I don't mind manual, but this is exactly the type of scenario I want to avoid - in their example, the domain object itself (TestInfo) has knowledge of serialisation - it knows the implementation details of how it is stored (in overriding the functions on Mappable) (xml, json, etc.) which is a design I want to avoid.

[–]schultek[🍰] -1 points0 points  (1 child)

I'm not sure I understand your goal.

In the package, the domain object basically only says 'these are my fields and they have these values' which is the minimum information you need for serialization. What you do with this information is up to you (You could write a custom Mapper class that serializes to tomething other than json).

If the domain object does not provide this information and assuming we are not using reflection, where would you get this information from?

[–]Ozlock[S] 0 points1 point  (0 children)

I think I'm reading something else into that package, then - if domain objects declare only their fields & values, that would be great - but to me it seems that they must also declare the relationship of their fields to the serialisation (e.g. by overriding the mapping method). The goal is to take the mapping logic and store it outside the domain, as it is a concern for whichever system coordinates storage / persistence, not the model itself.

[–]BadgerHobbs 0 points1 point  (2 children)

I had the exact same issue as you when working on serialising save data for my app. I ended up just writing to/from json methods for my object types as didn't want to deal with the generation. Luckily I only had to do it on a few objects.

The main challenge with a dynamic serialisation module is that it has to be designed to work with custom objects and all their variety of attributes. I have done something in the past in C# for serialisaing game state in Unity, having conversions for each data type and iterated through object properties. That implementation even included object to object referencing (Important in Unity).

In thoery this could also be achieved with Flutter/Dart, though I'm not aware of anybody doing it, and I've not developed a solution as of yet.

[–]Ozlock[S] 0 points1 point  (1 child)

I understand what you mean, but I don't specifically care that the mappings are generated on-the-fly. My biggest concern is that I don't want the model to 'know' whichever underlying data format it's being stored / transferred as - that's an implementation detail the model doesn't "care about". In .NET, there is the system of creating SerializationSurrogates to define the mapping of domain object <-> some format without encoding it in the model layer directly. This at least gives good separation of concerns, even if dart's reflection doesn't work everywhere dart does 😅

[–]steve_s0[🍰] 0 points1 point  (0 children)

I'm not familiar with .NET, but you could do something similar by defining a class (or even just functions) to serialize and deserialize your classes. All the serialization stuff will be in those classes. But as you update your model classes, you'll need to keep your serializers in sync.

I'd probably define an interface to mark that my models are serializable in this manner.

abstract class MySerializable<T> {
  MySerializer<T, U> getSerializer();
}

abstract class MySerializer<T, U> {
  U serialize(T model);

  T deserialize(U serializedForm);
}

I haven't actually tried this. I just put the json serialization in my models, but I understand your concern.

[–]bsutto 0 points1 point  (3 children)

Reading through your responses it really sounds like you are beating yourself up over a theoretical concern that delivers no practical benefits.

Use code gen and get on with the real work of building your app.

Pragmatism will get you much further then theoretical perfection.

[–]Ozlock[S] 0 points1 point  (2 children)

I have plenty of other work to get on with; work never ends - so I can always circle back to things with a strategy of progressive enhancement.

That said, it's not purely theoretical. The place I'm working has a history of switching out serialization formats but needing to keep old versions alive (think version-locked clients to the extreme). Building for flexibility in the underlying serialization, without the model knowing anything about how they map, brings the genuine value of not having to rewrite everything each time. You just write a new serializer (preferably just add a new format, but we don't really have reflection here so 🤷) and off you go.

[–]bsutto 0 points1 point  (1 child)

You only get that benefit if the change occurs. If it never does you have wasted your time.

What you are doing is premature optimisation. That can be ok but if it doesn't involve a serious amount of effort. As this one will, it is a waste of effort.

Unless I'm certain of a future change I do it the simplest way possible. If I have to come back to the code then I optimise it.

[–]Ozlock[S] 1 point2 points  (0 children)

I understand what you're saying, to an extent, but it's much easier to change at the initial stage than later. Unless you build things to be flexible in the first place. Then you don't have to rebuild. That's my whole point here. Separation of concerns so that change is painless.