如何投射从 json 创建的`_InternalLinkedHashMap`?

Ric*_*ler 3 json dart

有时,在 Dart 中遍历复杂的 json 文件时,如果我们能告诉我们的编辑器预期的结构是什么,以便我们可以最好地利用编辑器的智能代码完成功能,那就太好了。

作为一个玩具示例,请考虑脚本writer.dart

import 'dart:convert' show json;

main() {
  Map<String, num> myMap = {"a": 1, "b": 2, "c": 3};
  print(json.encode(myMap));
}
Run Code Online (Sandbox Code Playgroud)

假设我们使用 writer.dart 创建一个 json 文件:

dart writer.dart > data.json
Run Code Online (Sandbox Code Playgroud)

我们有另一个名为reader.dart 的脚本,它将读取此文件并将数据理想地解释为一个Map<String, num>实例:

import 'dart:io' show File;
import 'dart:convert' show json;

main() async {
  Map<String, num> myMap = json.decode(await File("data.json").readAsString());
}
Run Code Online (Sandbox Code Playgroud)

但是,此脚本会引发异常type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Map<String, num>'

像以下这样的幼稚尝试也不起作用:

var myMap = json.decode(await File("data.json").readAsString()) as Map<String, num>;
Run Code Online (Sandbox Code Playgroud)

当然,我们可以这样做:

var myMap = Map<String, num>();
(json.decode(await File("data.json").readAsString()) as Map).forEach((k, v) {
  myMap[k] = v;
});
Run Code Online (Sandbox Code Playgroud)

但是真的很丑!

让编辑器在解析 json 时知道期望什么数据结构的最佳方法是什么?

lrn*_*lrn 9

返回的地图json.decode是一个可变的Map<String, Object>。在这种情况下,您知道这些值都是num实例,但 JSON 解码器不知道这一点。即使是这样,由于该值是可变的,解码器必须假设有人稍后可能想要将任何对象添加到地图中。

有两种基本方法可以从另一个地图获取具有更严格类型的地图:复制到新地图(并在复制时检查更严格的类型),或包装/调整原始地图(并在每次访问时检查更严格的类型)。

您可以通过以下方式创建您想要的任何类型的新地图:

Map<String, num> newMap = Map<String, num>.from(originalMap);
Run Code Online (Sandbox Code Playgroud)

的参数Map.from是 a Map<dynamic, dynamic>,因此它确实接受任何映射,然后将每个条目复制到具有请求类型的新映射中。如果在制作副本后更改原始地图,则副本不受影响。

另一种选择是使用以下方法包装:

Map<String, num> wrappedMap = originalMap.cast<String, num>();
Run Code Online (Sandbox Code Playgroud)

这在前面没有任何作用。每次您尝试在wrapped映射中查找内容时,它都会根据您要求的类型检查参数类型和返回值类型。如果您更改原始地图,包装的地图也会发生变化(这是它不能只检查一次类型的另一个原因,它必须每次检查值是否已更改)。

这两种方法具有不同的效率权衡。复制一切都需要空间和时间。检查类型也需要时间。如果您打算一遍又一遍地使用相同的地图,或者对其进行修改,那么创建新地图可能会更有效。如果没有,Map.cast则更容易使用。