Are you sure that events then has the type List that I need?
also I can’t match on the the key of the nested map as that values can change on every API call.
My example is a simplified reproduction of what I to solve.
More over I would like to know why ‘cast’ doesn’t work here.
Cast is not convert. Cast is “This is this base type, but I’m telling you that this is this derived type”. You can NEVER “cast” something to other type (i.e.: int to String is convert, not cast).
Map without types is Map<dynamic>. You should NEVER use generics without type (or turn on strict-casts: true, strict-inference: true and strict-raw-types: true in analysis_options.dart to get warnings at least. Dart is pretty shitty regarding Generics. Then, again, cast is not convert, you can’t “cast” a Map<dynamic, dynamic> to Map<String, List<int>> because String does NOT inherit dynamic. In this case, you must to explicit convertion. Usually, jsonDecode returns a Map<String, dynamic> that can be used to, at least, get a reference to a value. But, then again, the result dynamic won’t be castable to List<int>, you’ll need to explicitly convert or, in Dart case, tell the compiler it is what you say it is (in this case, using the explicity cast operator as, which will result in a runtime exception when misused - it would be better to use patterns and verify for exceptional (hence, exception) cases.
The cast() method only casts one “level” deep into the collection. When you do:
Map<String, List<int>> cal =
(json['calendar']! as Map).cast<String, List<int>>();
You’re expecting it to both coerce the map to Map<String, List<int> but also cast all of the elements to List<int>. But those inner casts (which are like as, not .cast()) fail because the reified type of the list is List<dynamic>, not List<int>.
It’s annoying but you have to manually coerce at every level if you have nested JSON like this.
Sorry to correct you but that cast() function of maps is exactly to tell Dart that you know that this dynamic value is in reality a map of a certain type.
Which it correctly does if you do it only on one level.
Casting a dynamic or Object to any type is perfectly legal if you know what the real type is
@munificent but why? For my understanding the cast () method just function says to treat the map as if it where of the other type which will lead to run time errors if it isn’t.
I wound understand if I would try to cast it so some custom class but we are talking here about lists and simple types.
I C++ this would be like a reinterpret_cast.
Especially for generated code this makes dealing with deserialization why more difficult.
Granted, this is an interesting discussion and I may not have encountered a cast like this yet, but I think the idea of the reinterpret_cast in C++ is helpful in understanding this problem.
In C++ for example you can’t do:
int x = 0;
double y = reinterpret_cast<double>(x);
as in this discussion you can’t cast from dynamic to List<int>.
reinterpret_cast is used with pointers/references and the above code is not a reinterpretation. For example the correct way is to use something like:
double y = *reinterpret_cast<double*>(&x);
and within Dart I think is not possible to do it this way.
I don’t think this error is related to the nested nature of the json though. Because the following code which isn’t nested also fails with the same error, and I think the source is the same.
final List<dynamic> list = [123];
final arr = list as List<int>;
print(arr);
type ‘List’ is not a subtype of type ‘List’ in type cast
In your code when I catch the exception it fails at
void forEach(void f(K key, V value)) {
_source.forEach((SK key, SV value) {
f(key as K, value as V);
});
}
Here the value as V is an identical cast of List<dynamic> to List<int> as in my example
Hope someone can shine some light on why this as fails though.
What’s even weirder is that as doesnt work, but cast does work fine
final List<dynamic> list = [123];
// final arr = list as List<int>;
final arr = List.castFrom<dynamic, int>(list);
print(arr);
But based on any doc I could find, both are type cast in dart, so why the difference
No, that’s not what it does. If that’s all you wanted, you’d use as Map<String, List<int>>.
In reality, the map is a Map<String, dynamic>. It is not a Map<String, List<int>>:
import 'dart:convert';
final jsonString = '''{
"calendar":
{
"2024-11-18" : [601014]
}
}
''';
void main() {
final json = jsonDecode(jsonString) as Map<String, dynamic>;
final calendar = json['calendar']!;
print(calendar is Map<String, dynamic>); // Prints "true".
print(calendar is Map<String, List<int>>); // Prints "false".
}
Likewise, the lists inside there are List<dynamic> they are notList<int>:
import 'dart:convert';
final jsonString = '''{
"calendar":
{
"2024-11-18" : [601014]
}
}
''';
void main() {
final json = jsonDecode(jsonString) as Map<String, dynamic>;
final calendar = json['calendar']!;
final list = calendar['2024-11-18']!;
list.add('definitely not an int!');
print(list); // Prints "[601014, definitely not an int!]".
}
What cast() does is produce a new map with the desired key and value types. Then, when you access elements, it lazily casts (using as) each entry’s key and value to the expected types. That’s exactly and all that it does.
It doesn’t do any deep conversion. I don’t think it would be possible to implement a “deep cast” version of cast() that recursively calls cast() on the keys and values if they are types that support that. As far as I can tell, Dart doesn’t expose enough runtime type information (or compile-time specialization) to support that.