google / built_value.dart

Immutable value types, enum classes, and serialization.
https://pub.dev/packages/built_value
BSD 3-Clause "New" or "Revised" License
868 stars 184 forks source link

Deserialize to a Builder #1156

Open dotdoom opened 2 years ago

dotdoom commented 2 years ago

Problem statement

Automatically deserialize objects with additional information inferred from context. E.g.: deserialize an entity from database, then immediately assign "key" property which is not in object's JSON but is known to the code calling deserializer:

Example ```dart // { ... // "42abef": { // "name": "Andreas" // } // } class Person implements Built { String get name; // deserialized automatically. String? get key; // known to deserialize caller, but must be nullable to allow deserialization. static Serializer get serializer => _$personSerializer; } ```

Marking those extra fields nullable forces all uses to add a null check operator (person.key!).

Proposal

Deserialize to a Builder, rather than the model itself, then have the library user's code assign missing field and call .build():

final person = serializers.deserializeWith(Person.serializer, snapshot.value)..key = currentKey..build();

Alternatively, have the deserialize method take a Builder with some fields already assigned, then backfill it and ship back the ready model.

final person = serializers.deserializeWith(Person.serializer, PersonBuilder(key: currentKey), snapshot.value);
davidmorgan commented 2 years ago

Would it work to add a finalize hook to the builder?

https://pub.dev/documentation/built_value/latest/built_value/BuiltValueHook-class.html

It can fill in the key ... somehow. It's not pretty, but you could pass it via a private static field.

Partial deserialization with builders does seem a reasonable thing to support; will have a think about it next time I'm looking at features. That might be a while, though.

dotdoom commented 2 years ago

That definitely makes a workaround. For primitive types and modifiable snapshot.value (e.g. a modifiable Map) which will be discarded after deserialization anyway, one can also add those artificial values to the snapshot before deserializing.

But indeed both ways look rather hacky.