Flutter:使用json_serializable反序列化firebase_messaging通知遇错求助
我之前也踩过FCM消息反序列化的坑,结合你遇到的Type 'String' is not a subtype of type 'Map < dynamic, dynamic>'和_InternalLinkedHashMap< dynamic, dynamic>' is not a subtype of type 'Map< String, dynamic>'错误,咱们一步步来排查解决:
1. 先搞清楚FCM消息的实际结构
FCM的onMessage回调返回的msg是嵌套结构,通常包含notification(推送通知的标题、内容等)和data(自定义业务字段)两个核心部分。你直接把整个msg传给FCMNotification.fromJson()大概率是不对的——因为你的模型对应的应该是其中某一个子结构,而非整个消息体。
先打印消息结构确认:
onMessage: (Map<String, dynamic> msg) { print('完整FCM消息结构: $msg'); // 看控制台输出的字段,定位需要反序列化的部分 // 比如如果你的模型对应notification字段,就取这个子Map final notificationMap = msg['notification'] as Map<String, dynamic>; FCMNotification fcmNotification = FCMNotification.fromJson(notificationMap); }
2. 处理FCM data字段的字符串值陷阱
FCM的data字段有个坑:所有值默认都是String类型,哪怕你后台传的是数字或布尔值。如果你的FCMNotification模型里有非String字段(比如int badge、bool isRead),直接反序列化就会报类型不匹配。
解决办法是用json_serializable的自定义转换逻辑:
@JsonSerializable() class FCMNotification { // 给需要转换的字段加JsonKey,指定转换函数 @JsonKey(fromJson: _stringToInt, defaultValue: 0) final int badge; @JsonKey(fromJson: _stringToBool, defaultValue: false) final bool isRead; final String title; FCMNotification({required this.badge, required this.isRead, required this.title}); factory FCMNotification.fromJson(Map<String, dynamic> json) => _$FCMNotificationFromJson(json); Map<String, dynamic> toJson() => _$FCMNotificationToJson(this); } // 自定义字符串转int的函数 int _stringToInt(String? value) { if (value == null) return 0; return int.tryParse(value) ?? 0; } // 自定义字符串转bool的函数 bool _stringToBool(String? value) { if (value == null) return false; return value.toLowerCase() == 'true'; }
3. 显式转换嵌套Map的类型
有时候FCM返回的嵌套Map是_InternalLinkedHashMap类型,直接强转Map<String, dynamic>会失败。这时候可以用Map<String, dynamic>.from()来做类型转换:
onMessage: (Map<String, dynamic> msg) { // 处理notification字段 final rawNotification = msg['notification']; if (rawNotification != null) { final notificationMap = Map<String, dynamic>.from(rawNotification as Map); FCMNotification fcmNotification = FCMNotification.fromJson(notificationMap); } // 处理data字段同理 final rawData = msg['data']; if (rawData != null) { final dataMap = Map<String, dynamic>.from(rawData as Map); // 用dataMap反序列化对应的模型 } }
4. 检查模型字段与FCM字段的匹配度
json_serializable默认是蛇形命名与驼峰命名自动转换(比如模型里的notificationTitle对应JSON里的notification_title),但如果FCM返回的字段名和你模型的字段名不匹配(比如大小写不一致、命名规则不同),也会导致反序列化失败。
这时候需要用@JsonKey(name: "xxx")指定对应关系:
@JsonSerializable() class FCMNotification { // FCM返回的字段是"notification_title",模型里用驼峰 @JsonKey(name: "notification_title") final String notificationTitle; FCMNotification({required this.notificationTitle}); // ... 其他代码 }
按照上面的步骤排查,应该能解决你遇到的类型转换问题。
内容的提问来源于stack exchange,提问作者Thomas Nicole

