为什么 json 对象的一种方式有效而另一种方式抛出 null 错误? Dart/Flutter null 安全问题

Why is one way of json to object working and the other throwing null error? Dart/Flutter null safety trouble

我正在学习如何使用 json,不习惯 Dart 的空安全性,目前为止我不喜欢它。但我别无选择,只能适应它。我正在尝试将我的 json 对象列表解析为对象列表。我能够在我的 main 中计算出基础知识,但是当我尝试使用相同的结构在 class 中创建一个实际方法时,我得到了一个空错误。据我所知,我在两者中都做了完全相同的事情,只是添加了用于迭代整个 json 列表的循环。

注意:我当然尝试插入可选的 ?它在哪里询问,但 IDE 不允许这样做。

谁能帮我解释一下我做错了什么?

class 方法错误 jsonToDatabaseSyncItem()

lib/services/remote_database_services.dart:52:43: Error: Property 'length' cannot be accessed on 'List<dynamic>?' because it is potentially null.
 - 'List' is from 'dart:core'.
Try accessing using ?. instead.
    final jsonListLength = jsonObjectList.length;
                                          ^^^^^^
lib/services/remote_database_services.dart:55:38: Error: Operator '[]' cannot be called on 'List<dynamic>?' because it is potentially null.
 - 'List' is from 'dart:core'.
      var jsonObject = jsonObjectList[index]['DatabaseSyncItem'];

Class 方法(class- RemoteDatabaseService)

// This method will get server database
  Future<List<dynamic>?> getRemoteDatabase() async {
    final events = QueryBuilder<ParseObject>(ParseObject('Event'));
    final apiResponse = await events.query();
    if (apiResponse.success && apiResponse.result != null) {
      return apiResponse.results;
    } else {
      return [];
    }
  }

// Method to parse json result list back to objects
  Future<List<dynamic>?> jsonToDatabaseSyncItem() async {
    final remoteDatabaseList = await getRemoteDatabase();
    final jsonObjectList = await Future.value(remoteDatabaseList);     /// This method throws the above
    final jsonListLength = jsonObjectList.length;                      /// error when run in main

    for (var index = 0; index == jsonListLength; index++) {
      var jsonObject = jsonObjectList[index]['DatabaseSyncItem'];
      print(jsonObject);
    }
  }

主文件工作代码

Future<void> main(List<String> arguments) async {

final test = remoteDatabaseServices.getRemoteDatabase();
  Future<List<dynamic>?> getList() {
    return Future.value(test);
  }

  var list = await getList();
  print(list?.length);
  var jsonObject = list![0]['DatabaseSyncItem'];
  print(jsonObject);

  var toObject = DatabaseSyncItem.fromJson(jsonObject);
  print(toObject);
}

你的 null-safety 问题似乎是缺乏对该功能的理解。我会建议您阅读此处的文档并阅读所有章节:https://dart.dev/null-safety

关于您的代码,您应该考虑什么时候可以 null 以及什么时候可以检查 null 并处理这种情况。在您的示例中,如果发生错误或没有结果 return,似乎 getRemoteDatabase() 应该只是 return 一个空的 List。所以我们不需要把这个方法的 return 类型设置为 Future<List<dynamic>?> 如果我们稍微重写这个方法:

Future<List<dynamic>> getRemoteDatabase() async {
  final events = QueryBuilder<ParseObject>(ParseObject('Event'));
  final apiResponse = await events.query();
  if (apiResponse.success) {
    return apiResponse.results ?? <dynamic>[];
  } else {
    return <dynamic>[];
  }
}

(?? 运算符将在这里测试 apiResponse.results 是否为 null,如果是,我们 return <dynamic>[]。如果不是,我们使用值apiResponse.results).

由于此方法现在保证永远不会 return null,我们可以使用它来简化下一个方法。我还重写了它以使用 for-each 循环,因为我们真的不需要每个元素的 index

Future<void> jsonToDatabaseSyncItem() async {
  final jsonObjectList = await getRemoteDatabase();

  for (final jsonObject in jsonObjectList) {
    print(jsonObject['DatabaseSyncItem']);
  }
}

我也删除了这一行,因为它什么都不做。如果 remoteDatabaseList 是一个 Future 你应该只是 await 而不是创建一个新的 Future.

final jsonObjectList = await Future.value(remoteDatabaseList);

此外,jsonToDatabaseSyncItem() 的 return 类型已更改为 Future<void>,因为我们从不 returning 任何值。

我对你的 main 有点困惑,但我认为这是最明显的地方,你对 Future 和 null-safety 感到困惑。我试图重写它,使它更干净,但仍然应该这样做:

Future<void> main(List<String> arguments) async {
  final list = await getRemoteDatabase();
  print(list.length);
  
  final dynamic jsonObject = list[0]['DatabaseSyncItem'];
  print(jsonObject);

  final toObject = DatabaseSyncItem.fromJson(jsonObject);
  print(toObject);
}