Flutter继承类如何正确生成Json_Serializable序列化代码
我定义了两个基类Student和Room,二者分别对应派生的模型类StudentModel和RoomModel。在模型类上使用json_serializable库生成JSON转换逻辑时,执行build_runner生成代码报错,相关代码和报错信息如下。
相关代码
import 'package:equatable/equatable.dart'; import 'package:json_annotation/json_annotation.dart'; part 'student.g.dart'; /// * Student & StudentModel class Student extends Equatable { final int id; final String name; final Room studentRoom; const Student({this.id = 0, this.name = "", this.studentRoom = const Room()}); @override List<Object?> get props => [id, name]; } @JsonSerializable(explicitToJson: true) class StudentModel extends Student { const StudentModel({int id = 0, String name = "", RoomModel studentRoom = const RoomModel()}); factory StudentModel.fromJson(Map<String, dynamic> json) => _$StudentModelFromJson(json); Map<String, dynamic> toJson() => _$StudentModelToJson(this); } /// * Room & RoomModel class Room extends Equatable { final int id; final String roomName; const Room({this.id = 0, this.roomName = ""}); @override List<Object?> get props => [id, roomName]; } @JsonSerializable() class RoomModel extends Room { const RoomModel({int id = 0, String roomName = ""}) : super(id: id, roomName: roomName); factory RoomModel.fromJson(Map<String, dynamic> json) => _$RoomModelFromJson(json); Map<String, dynamic> toJson() => _$RoomModelToJson(this); }
build_runner报错信息
Could not generate `toJson` code for `studentRoom`. To support the type `Room` you can: * Use `JsonConverter` * Use `JsonKey` fields `fromJson` and `toJson` package:untitled1/model/student.dart:11:14 ╷ 11 │ final Room studentRoom; │ ^^^^^^^^^^^ ╵
json_serializable生成代码时是按照字段的声明类型查找序列化方法的,不会识别构造函数里传入的实际子类类型。父类Student中studentRoom字段声明为Room类型,而Room类本身没有添加@JsonSerializable注解、也没有对应的序列化逻辑,构建器找不到Room类型的fromJson/toJson方法就会抛出错误。直接在子类上加@JsonKey配置无法覆盖父类已经声明的字段类型,所以之前的尝试没有生效。
以下3种方案均可解决问题,按需选择即可:
方案1(最推荐):使用泛型重构基类,类型安全无冗余
把基类中关联的Room类型改为泛型参数,让子类在继承时明确指定关联类型为RoomModel,构建器就能直接识别到正确的可序列化类型。
修改后的核心代码示例:// 基类改为泛型 class Student<T extends Room> extends Equatable { final int id; final String name; final T studentRoom; const Student({this.id = 0, this.name = "", required this.studentRoom}); @override List<Object?> get props => [id, name]; } // 子类继承时明确指定泛型为RoomModel @JsonSerializable(explicitToJson: true) class StudentModel extends Student<RoomModel> { const StudentModel({ int id = 0, String name = "", RoomModel studentRoom = const RoomModel() }) : super(id: id, name: name, studentRoom: studentRoom); factory StudentModel.fromJson(Map<String, dynamic> json) => _$StudentModelFromJson(json); Map<String, dynamic> toJson() => _$StudentModelToJson(this); }改完重新执行
flutter pub run build_runner build --delete-conflicting-outputs即可正常生成代码。方案2:自定义JsonConverter做类型转换
如果不想改动原有基类结构,可以写一个专门的转换器处理Room类型和JSON的转换,逻辑里手动判断实际类型调用RoomModel的序列化方法。
代码示例:class RoomConverter extends JsonConverter<Room, Map<String, dynamic>> { const RoomConverter(); @override Room fromJson(Map<String, dynamic> json) { return RoomModel.fromJson(json); } @override Map<String, dynamic> toJson(Room object) { if (object is RoomModel) { return object.toJson(); } // 兜底处理普通Room实例的序列化 return { "id": object.id, "roomName": object.roomName, }; } }之后把转换器加到
StudentModel类上即可:@JsonSerializable(explicitToJson: true) @RoomConverter() class StudentModel extends Student { // 其余代码保持不变 }方案3:在子类中覆写studentRoom字段显式指定类型
在子类中重新声明studentRoom字段,明确标注类型为RoomModel,构建器会优先识别子类覆写的字段生成序列化逻辑,注意构造函数要正确给覆写的字段赋值。
代码示例:@JsonSerializable(explicitToJson: true) class StudentModel extends Student { @override final RoomModel studentRoom; const StudentModel({int id = 0, String name = "", this.studentRoom = const RoomModel()}) : super(id: id, name: name, studentRoom: studentRoom); factory StudentModel.fromJson(Map<String, dynamic> json) => _$StudentModelFromJson(json); Map<String, dynamic> toJson() => _$StudentModelToJson(this); }
内容的提问来源于stack exchange,提问作者Sapatekno

