Dart中json_annotation继承场景下子类实例反序列化实现方法咨询
解决json_annotation多态反序列化问题
你遇到的是json_annotation默认不支持多态反序列化的问题——基类的fromJson只会生成基类实例,无法自动识别子类类型。下面提供两种可行的实现方式:
方式一:自定义基类fromJson方法(简单直接)
步骤1:给基类添加类型标记字段
在UIWidget中新增type字段,用来标识具体的子类类型,每个子类对应唯一的字符串值:
@JsonSerializable(generateFactory: false) // 禁用自动生成fromJson,因为我们要自定义 class UIWidget { double? size = 50; List<UIWidget> children = List.empty(growable: true); // 类型标记,每个子类对应唯一值 final String type; UIWidget({required this.type}); // 自定义fromJson,根据type字段创建对应子类实例 factory UIWidget.fromJson(Map<String, dynamic> json) { switch (json['type'] as String) { case 'UIGridView': return UIGridView.fromJson(json); // 后续新增子类时,在这里添加对应的case default: throw ArgumentError('未知的Widget类型: ${json['type']}'); } } // toJson仍使用自动生成的代码 Map<String, dynamic> toJson() => _$UIWidgetToJson(this); }
步骤2:子类初始化类型标记
每个子类在构造函数中初始化父类的type字段,确保序列化时携带类型信息:
@JsonSerializable(explicitToJson: true) class UIGridView extends UIWidget { int scrollDirection = Axis.vertical.index; String _showAxis = "Vertical"; int crossAxisCount = 3; double mainAxisSpacing = 0.0; double crossAxisSpacing = 0.0; double childAspectRatio = 1.0; // 初始化父类的type字段 UIGridView() : super(type: 'UIGridView'); factory UIGridView.fromJson(Map<String, dynamic> json) => _$UIGridViewFromJson(json); @override Map<String, dynamic> toJson() => _$UIGridViewToJson(this); }
效果
当反序列化children时,调用的UIWidget.fromJson会根据每个元素的type字段自动创建对应子类的实例,而非基类实例。
方式二:使用JsonConverter(更灵活,适合多子类场景)
如果后续要添加大量子类,使用转换器可以把多态逻辑和类本身解耦:
步骤1:创建UIWidget转换器
class UIWidgetConverter implements JsonConverter<UIWidget, Map<String, dynamic>> { const UIWidgetConverter(); @override UIWidget fromJson(Map<String, dynamic> json) { final type = json['type'] as String; switch (type) { case 'UIGridView': return UIGridView.fromJson(json); // 新增子类时扩展这里 default: throw ArgumentError('未知的Widget类型: $type'); } } @override Map<String, dynamic> toJson(UIWidget object) { if (object is UIGridView) { return object.toJson(); } // 新增子类时扩展这里 throw ArgumentError('无法序列化的Widget类型: ${object.runtimeType}'); } }
步骤2:给基类的children字段添加转换器标记
@JsonSerializable() class UIWidget { double? size = 50; // 给children字段指定转换器 @JsonKey(converter: UIWidgetConverter()) List<UIWidget> children = List.empty(growable: true); final String type; UIWidget({required this.type}); factory UIWidget.fromJson(Map<String, dynamic> json) => _$UIWidgetFromJson(json); Map<String, dynamic> toJson() => _$UIWidgetToJson(this); }
步骤3:子类初始化type字段(同方式一)
每个子类仍需在构造函数中初始化父类的type字段,确保序列化时携带类型标记。
这种方式的优势是多态逻辑集中在转换器中,类本身的代码更简洁,后续新增子类只需修改转换器即可。
内容的提问来源于stack exchange,提问作者Magesh
相关产品推荐
相关产品推荐

