0

I'm learning how to use json, not used to Dart null safety & I don't like it so far. But I will have no choice but to adapt to it. I'm trying to parse my json object list into a list of objects. I was able to work out the basics in my main, but when I attempt to create an actual method in a class using the same structure I'm getting a null error. As far as I can tell I'm doing the exact same thing in both with addition of the loop for iterating the entire json list.

Note: I of course did try inserting the optional ? where it asks but the IDE will not allow this.

Can someone help with explaining what I'm doing wrong here?

Error for class method 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 method (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);
    }
  }

Main file working code

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);
}
RobbB
  • 1,214
  • 11
  • 39

1 Answers1

1

Your problem with null-safety seems to be a missing understanding about the feature. I will recommend you to read through the documentation here and read all the chapters: https://dart.dev/null-safety

About your code, you should consider when something can be null and when you can check the null and handle that case. In your example, it seems like getRemoteDatabase() should just return an empty List if an error happens or no result is returned. So we don't need to have the return type of this method as Future<List<dynamic>?> if we rewrite the method a bit:

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>[];
  }
}

(the ?? operator will here test if apiResponse.results is null, if that is the case, we return <dynamic>[]. If not, we use the value of apiResponse.results).

Since this method is now guarantee to never return null, we can use that to simplify the next method. I have also rewritten it to use a for-each loop since we don't really need the index of each element.

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

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

I have also removed this line since it does nothing at all. If remoteDatabaseList is a Future you should just await on that instead of creating a new Future.

final jsonObjectList = await Future.value(remoteDatabaseList);

Also, the return type of jsonToDatabaseSyncItem() have been changed to Future<void> since we are never returning any value.

I am a little confused about your main but I think this is where it is most clear that you got a confused about Future and null-safety. I have tried to rewrite it so it is much cleaner but should still do the same:

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);
}
julemand101
  • 28,470
  • 5
  • 52
  • 48
  • 1
    Fantastic answer :) this is what a good SO answer should look like IMO. I'm a programming noob and I sincerely appreciate the didactic attitude. Ill be giving the docs a read over the next few days. Thanks for the help julemand. Also, you were spot on about my confusion. I did learn a bit about null safety with Swift but using it with Futures also threw me a curve. So good to know. – RobbB Jun 12 '21 at 02:51