Flutter解决'type 'Null' is not a subtype of Map<String,dynamic>'错误
问题:Flutter解析含Null字段的JSON时报错
type 'Null' is not a subtype of Map<String dynamic> 问题背景
调用接口获取JSON对象列表时,部分返回的JSON中userID字段为null,使用手动编写的解析代码转换数据时,抛出type 'Null' is not a subtype of Map<String dynamic>错误。
示例JSON结构
{ "_id": "62dd4989eef1e8863eb65ffe", "shoopkeper": { "_id": "62d78910a0d65589c3d112af", "userName": "lakshya", "location": { "lat": 23.2403327, "lng": 77.4389773 } }, "offerId": { "_id": "62da41208be0d1f7a092e4ea", "offername": "thisisoffername", "imageURL": "" }, "userID": { "_id": "62d5038dd7d46f3c025a0d3f", "userName": "Mahak " }, "state": 0, "createdAt": "2022-07-24T13:30:49.534Z", "updatedAt": "2022-07-24T13:30:49.534Z", "__v": 0 }
注:部分返回的JSON中userID字段为null
当前JSON解析代码
class CollectedOffer { CollectedOffer({ this.id, this.shoopkeper, this.offerId, this.userId, this.state, this.createdAt, this.updatedAt, this.v, this.redeemDate, }); String? id; Shoopkeper? shoopkeper; OfferId? offerId; UserId? userId; int? state; DateTime? createdAt; DateTime? updatedAt; int? v; DateTime? redeemDate; factory CollectedOffer.fromJson(Map<String, dynamic> json) => CollectedOffer( id: json["_id"], shoopkeper: Shoopkeper.fromJson(json["shoopkeper"]), offerId: OfferId.fromJson(json["offerId"]), userId: UserId.fromJson(json["userID"]), // 核心问题:当json["userID"]为null时,强行调用fromJson会触发类型错误 state: json["state"], createdAt: DateTime.parse(json["createdAt"]), updatedAt: DateTime.parse(json["updatedAt"]), v: json["__v"], redeemDate: DateTime.parse(json["redeemDate"]), // 额外问题:redeemDate字段不存在或为null时,此处会崩溃 ); } class OfferId { OfferId({ this.id, this.offername, this.imageUrl, }); String? id; String? offername; String? imageUrl; factory OfferId.fromJson(Map<String, dynamic> json) => OfferId( id: json["_id"], offername: json["offername"], imageUrl: json["imageURL"], ); } class Shoopkeper { Shoopkeper({ this.id, this.userName, this.location, }); String? id; String? userName; Location? location; factory Shoopkeper.fromJson(Map<String, dynamic> json) => Shoopkeper( id: json["_id"], userName: json["userName"], location: Location.fromJson(json["location"]), ); } class Location { Location({ this.lat, this.lng, }); double? lat; double? lng; factory Location.fromJson(Map<String, dynamic> json) => Location( lat: json["lat"], lng: json["lng"], ); } class UserId { UserId({ this.id, this.userName, }); String? id; String? userName; factory UserId.fromJson(Map<String, dynamic> json) => UserId( id: json["_id"], userName: json["userName"], ); }
错误信息
type 'Null' is not a subtype of Map<String dynamic>
FutureBuilder相关代码
if (response.statusCode == 200) { jsonResponse = json.decode(response.body)['objects']; if (jsonResponse.isNotEmpty) { setState(() { isdataNull = false; }); } return jsonResponse.map((data) => CollectedOffer.fromJson(data)).toList(); } else { throw Exception('${response.statusCode}'); }
解决方案
1. 修复userID字段的null处理
在CollectedOffer.fromJson方法中,先判断json["userID"]是否为null,仅当它不为null时才调用UserId.fromJson,否则直接赋值为null:
factory CollectedOffer.fromJson(Map<String, dynamic> json) => CollectedOffer( id: json["_id"], shoopkeper: Shoopkeper.fromJson(json["shoopkeper"]), offerId: OfferId.fromJson(json["offerId"]), userId: json["userID"] != null ? UserId.fromJson(json["userID"]) : null, // 新增null判断 state: json["state"], createdAt: DateTime.parse(json["createdAt"]), updatedAt: DateTime.parse(json["updatedAt"]), v: json["__v"], redeemDate: json["redeemDate"] != null ? DateTime.parse(json["redeemDate"]) : null, // 同时修复redeemDate的null问题 );
2. 可选:为其他可能为null的字段添加防护
如果接口返回的shoopkeper或offerId也存在为null的情况,同样需要添加null判断:
shoopkeper: json["shoopkeper"] != null ? Shoopkeper.fromJson(json["shoopkeper"]) : null, offerId: json["offerId"] != null ? OfferId.fromJson(json["offerId"]) : null,
3. 推荐:使用json_serializable自动生成解析代码
手动编写解析代码容易遗漏null判断,推荐使用官方推荐的json_serializable库,它会自动处理null字段和类型转换:
- 添加依赖到
pubspec.yaml:dependencies: json_annotation: ^4.8.1 dev_dependencies: build_runner: ^2.4.4 json_serializable: ^6.7.0 - 改造实体类并添加注解:
import 'package:json_annotation/json_annotation.dart'; part 'collected_offer.g.dart'; @JsonSerializable() class CollectedOffer { CollectedOffer({ this.id, this.shoopkeper, this.offerId, this.userId, this.state, this.createdAt, this.updatedAt, this.v, this.redeemDate, }); @JsonKey(name: '_id') String? id; Shoopkeper? shoopkeper; OfferId? offerId; @JsonKey(name: 'userID') UserId? userId; int? state; @JsonKey(fromJson: _dateTimeFromJson) DateTime? createdAt; @JsonKey(fromJson: _dateTimeFromJson) DateTime? updatedAt; @JsonKey(name: '__v') int? v; @JsonKey(fromJson: _dateTimeFromJson) DateTime? redeemDate; factory CollectedOffer.fromJson(Map<String, dynamic> json) => _$CollectedOfferFromJson(json); Map<String, dynamic> toJson() => _$CollectedOfferToJson(this); } // 日期转换工具方法 DateTime? _dateTimeFromJson(String? dateStr) { if (dateStr == null) return null; return DateTime.parse(dateStr); } // 其他实体类同样添加@JsonSerializable()注解和对应的fromJson/toJson方法,示例省略 - 运行命令生成解析代码:
flutter pub run build_runner build
内容的提问来源于stack exchange,提问作者Arjun Malhotra
相关产品推荐
相关产品推荐

