Ahmed Wagdi
Ahmed Wagdi

Reputation: 4391

Unhandled Exception: Cyclic error in JSON stringify in Dart

Am trying to access a model from inside it is parents model , so I get this error :

Unhandled Exception: Cyclic error in JSON stringify

Here is my code :

class User {
  bool? success;
  String? message;
  String? token;
  Data? data;

  User({this.success, this.message, this.token, this.data});

  User.fromJson(Map<String, dynamic> json) {
    success = json['success'];
    message = json['message'];
    token = json['token'];
    data = json['data'] != null ? Data.fromJson(json['data']) : null;
  }

  Map<String, dynamic> toJson() {
    final data = <String, dynamic>{};
    data['success'] = success;
    data['message'] = message;
    data['token'] = token;
    data['data'] = data; //Here is the problem
    return data;
  }
}

class Data {
  int? id;
  int? mainUser;
  String? deviceToken;
  String? name;
  bool? isLogged;
  int? activationCode;
  String? mobileNumber;
  bool? isVerified;
  bool? isActive;
  int? code;

  Data(
      {this.id,
      this.mainUser,
      this.deviceToken,
      this.name,
      this.isLogged,
      this.activationCode,
      this.mobileNumber,
      this.isVerified,
      this.isActive,
      this.code});

  Data.fromJson(Map<String, dynamic> json) {
    id = json['id'];
    mainUser = json['main_user'];
    deviceToken = json['device_token'];
    name = json['name'];
    isLogged = json['is_logged'];
    activationCode = json['activation_code'];
    mobileNumber = json['mobile_number'];
    isVerified = json['is_verified'];
    isActive = json['is_active'];
    code = json['code'];
  }

  Map<String, dynamic> toJson() {
    final data = <String, dynamic>{};
    data['id'] = id;
    data['main_user'] = mainUser;
    data['device_token'] = deviceToken;
    data['name'] = name;
    data['is_logged'] = isLogged;
    data['activation_code'] = activationCode;
    data['mobile_number'] = mobileNumber;
    data['is_verified'] = isVerified;
    data['is_active'] = isActive;
    data['code'] = code;
    return data;
  }
}

Upvotes: 3

Views: 866

Answers (1)

julemand101
julemand101

Reputation: 31219

Your problem is related to scoping rules and what variable we are referring to when we use the name of a variable. When you are using a variable name, Dart will try and find a variable with that name starting from the scope you are in. If Dart don't find any variable with that name, it will search in a broader level of scope:

// Scope Level 0 (global scope)

class User {
  // Scope Level 1 (class scope)
  Data? data;

  Map<String, dynamic> toJson() {
    // Scope Level 2 (method scope)
    final data = <String, dynamic>{};
    data['data'] = data;
    return data;
  }
}

So when you are using data in data['data'] = data, Dart will start looking inside the scope of the method toJson(). Here it finds the variable data defined as final data = <String, dynamic>{} and uses that without even looking at the variable inside your User class.

The error you are getting is referring to the problem that your returned Map contains a cyclic reference since it contains a reference to the map itself (data['data'] points to the map itself which then contains a data key which contains the map with a data key and so on...). The problem with this kind of Map is that we cannot represent it with traditional JSON which makes Dart throw an exception since what you are trying to do cannot be achieve.

There are multiple solutions to this problem:

Use this. to point to class variable

In situations where we want to refer to the class variable instead of a same-named method variable, we can prefix the reference with this. which tells Dart to look in the class scope for the variable:

  Map<String, dynamic> toJson() {
    final data = <String, dynamic>{};
    data['success'] = success;
    data['message'] = message;
    data['token'] = token;
    data['data'] = this.data;
    return data;
  }

Use another name for the map

Another solution would be to prevent the confusion by not, in this case, use a variable name in the toJson() method which are also a class variable. E.g. we could rename data to map:

  Map<String, dynamic> toJson() {
    final map = <String, dynamic>{};
    map['success'] = success;
    map['message'] = message;
    map['token'] = token;
    map['data'] = data;
    return map;
  }

Don't use any variable at all for the map

In Dart, we have a shorthand syntax for methods which are just going to return a value (=>). We can use that to create a method which declare a Map with the needed key-value pairs and then immediately return it:

  Map<String, dynamic> toJson() => <String, dynamic>{
        'success': success,
        'message': message,
        'token': token,
        'data': data,
      };

Upvotes: 4

Related Questions