You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 05:45:39