'How to prevent Function cast Exceptions in Dart
I couldn't figure out how to store functions while keeping the storage agnostic about the functions' parameter type. I try to implement a json registry used for encoding/decoding. Decoding works fine but when it comes to encoding I get this error on runtime
E/flutter ( 4637): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: type '(QuotationRequest) => Map<String, dynamic>' is not a subtype of type '(Object) => Map<String, dynamic>'
E/flutter ( 4637): #0 JsonifyRegistry.encode (package:ridecube/src/commons/io/jsonify.dart:31:21)
...
when I call this:
final registry = JsonifyRegistry()
..register<QuotationRequest>(encoder: (_) => _.toJson(), decoder: (_) => QuotationRequest.fromJson(_));
final QuotationRequest data = QuotationRequest();
registry.encode(data); // <-- issue here
Hence, I'm curious how to do proper casting when it comes to dart function objects if possible.
// jsonify.dart
typedef Json = Map<String, dynamic>;
typedef JsonifyEncoder<T> = Json Function(T object);
typedef JsonifyDecoder<T> = T Function(Json encoded);
class JsonifyRegistryEntry<T> {
JsonifyRegistryEntry({required this.encoder, required this.decoder});
final JsonifyEncoder<T> encoder;
final JsonifyDecoder<T> decoder;
}
class JsonifyRegistry {
final Map<Type, JsonifyRegistryEntry<Object>> _reg = {};
void register<T>({required JsonifyEncoder<T> encoder, required JsonifyDecoder<T> decoder}) =>
registerEntry(JsonifyRegistryEntry<T>(encoder: encoder, decoder: decoder));
void registerEntry<T>(JsonifyRegistryEntry<T> entry) {
_reg[T] = entry as JsonifyRegistryEntry<Object>;
}
T decode<T>(Json encoded) {
assert(_reg.containsKey(T), 'unknown type for encoding/decoding. You need to register $T first!');
return _reg[T]!.decoder(encoded) as T;
}
Json encode<T>(T object) {
assert(_reg.containsKey(T), 'unknown type for encoding/decoding. You need to register $T first!');
return _reg[T]!.encoder(object!); //line 31
}
}
Note
I know why this is prevented of course but I'm still trying to find a solution for my implementation.
simple explanation:
typedef Fu1 = int Function(Object);
typedef Fu2 = int Function(int);
void main() {
final Fu2 fu2 = (x) => x;
final Fu1 fu1 = fu2 as Fu1; // error here Closure 'main_closure': type '(int) => int' is not a subtype of type '(Object) => int'
// if this wasn't the case you would get
// an error as soon as you make a call like
// this: fu1('string');
}
Solution 1:[1]
this is one possible solution but I don't like that I had to replace the property encoder by a method.. Maybe you guys can come up with a better advice.
class JsonifyRegistryEntry<T> {
JsonifyRegistryEntry({required JsonifyEncoder<T> encoder, required this.decoder}): _encoder = encoder;
final Function _encoder;
JsonifyEncoder<E> encoder<E>() => _encoder as JsonifyEncoder<E>;
final JsonifyDecoder<T> decoder;
}
// in JsonifyRegistry:
Json encode<T>(T object) {
assert(_reg.containsKey(T), 'unknown type for encoding/decoding. You need to register $T first!');
return _reg[T]!.encoder<T>()(object!);
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Osman |
