Reputation: 441
In Swift I'm used to setting up a protocol JSONInitializable
which defines an init?(json: JSON)
meaning that all classes and structs that conform to the protocol are initializable with a JSON object.
Is it true that this isn't possible in Dart (with an abstract class
) because static methods and factory initializers are not inherited from an abstract class?
The reason I'm asking is because I'm writing a lot of similar methods for API GET requests, which could easily be merged into one, e.g.:
static Future<T> get<T extends JSONInitializable>(int id) async {
final resourceName = T; // TODO: transform to snake case
final uri = Uri.parse("$kApiHostname/api/$resourceName/$id");
final response = await _secureGet(uri);
if (response == null) {
return null;
}
final responseJson = json.decode(response.body);
final model = T.fromJson(responseJson);
return model;
}
But for that to work I'd need to constrain T
to a protocol/interface that defines the .fromJson()
initializer.
Upvotes: 12
Views: 4642
Reputation: 990
Unfortunately Dart doesn't support some very important feature for this goal: calls of static methods from generic parameters. E.g. in C++ templates (which not a generics though) you can write code like this:
template<typename T>
T deserialize(MyStream istrm) { return T.deserialize(istrm); }
But as it was mentioned in previous answers we can't do the same in dart.
Currently in my solutions I use reflection.
Even though dart has builtin mirrors, but (suddenly) it is not supported in flutter. So we're asked to use reflectable package in flutter instead. Can you feel it? Trying to solve one issue we're bumping into another one. Nevertheless.
Using reflection, you can scan your modules for classes with particular annotation (e.g. @my_deserializable
), and then collect for static methods. Note, that you can write your own annotations.
Thus you can build-up a factories cache, smth like Map<Type, Function(MyStream istrm)> deserializers
. So having such factory at hands you then can use it as follows:
deserializers[T]!(responseJson);
Upvotes: 0
Reputation: 35
This Answer might be helpful. you can simply pass your fromJson constructor as a parameter.
Simple steps I followed
create an abstract class
abstract class BaseModel{ factory BaseModel.fromJson(Map<String, dynamic> json) { throw UnimplementedError(); } Map<String, dynamic>? toJson(); }
implement the base class
@JsonSerializable() class ServerStatus implements BaseModel { ServerStatus(this.status, this.message); String status = ''; String message = '';
@override factory ServerStatus.fromJson(Map<String, dynamic> json) => _$ServerStatusFromJson(json);
@override Map<String, dynamic>? toJson() => _$ServerStatusToJson(this); }
In your logic class where you are trying to access your fromJson method
class CustomHttp { CustomHttp(this. Constructor); T Function(Map<String, dynamic>) constructor; Future<Object?> get(String path) async { final response = await http.get(this.uri); final Map<String, dynamic> parsed = json.decode(response. Body); return constructor(parsed); } }
Upvotes: 0
Reputation: 5733
A MANUAL workaround could be having a map of serializers,deserializers like:
//Register object 1 in singleton
JsonMapper.register<MyObject>(JsonObjectMapper(
(mapper, map) => MyObject.fromJson(map),
(mapper, instance) => instance.toJson(),
));
//Register object 2 in singleton...
...
This way you can deserialize and serialize your objects as long as you have them registered without resorting to it's generic type.
The AUTOMATIC (technically code generated) way would be using a package like simple_json to help you workaround this with a little code generation so you don't mess up registering and eliminating mappers.
And the big plus is that the actual code that transforms your objecto from and to JSON is not stored in the object itself but in the generated classes, thus pushing the responsability of serializaition deserailization from the object into the generated code (in a decoupled manner).
Take my advice with a grain of salt as with both approaches you lose static type checking of wether a type has a registered mapper and can be converted.
Upvotes: 0
Reputation: 6161
The feature you want is not available (or planned) in Dart, but there have been discussions on it.
Give this issue a thumbs up: https://github.com/dart-lang/language/issues/356
Upvotes: 1