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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 13:09:18