在 Dart 中使用 json_serializer 将映射序列化为 JSON 的正确方法是啥?

Posted

技术标签:

【中文标题】在 Dart 中使用 json_serializer 将映射序列化为 JSON 的正确方法是啥?【英文标题】:What's the proper way to handle serializing maps to JSON using json_serializer in Dart?在 Dart 中使用 json_serializer 将映射序列化为 JSON 的正确方法是什么? 【发布时间】:2019-09-15 12:05:33 【问题描述】:

我一直在尝试通过调用我的类的 toJson 函数来返回一个 Map,但我得到了 NoSuchMethodError:类“_InternalLinkedHashMap”没有实例方法“toJson”。 所以我假设这是不正确的。 examples 仅包含可以转换为单个值的单个值,但不处理列表或映射。 那么在 Dart 中使用 json_serializer 将映射序列化为 JSON 的正确方法是什么?

我要序列化的类:

class StudentDailyReport 
  final DocumentReference documentReference;
  Student student;

  // CheckIn
  String attendanceStatus;
  bool checkInChanged;
  DateTime checkedInTime;

  DateTime date;

  // CheckOut
  bool get checkedOut => this.attendanceStatus == AttendanceStatus.checkedOut;
  bool checkOutChanged;
  DateTime checkedOutTime;

  // Mood
  Mood mood;
  bool moodHasChanged;
  TextEditingController moodController;

  // Supplies
  Supplies supplies;

  // Notes
  Note note;
  TextEditingController noteController;
  bool noteChanged;

  // Meals
  Map<Meal, MealRecord> mealRecords;
  bool mealsChanged;

  // Health
  Map<int, HealthEntry> healthEntries;
  bool healthChanged;

  //PottyLog
  PottyLog pottyLog;

  bool get pottyLogChanged => pottyLog.hasChanged;

  ActivityLog activityLog;

  bool get activityLogChanged => activityLog.hasChanged;

  set activityLogChanged(bool value) =>
      activityLog = activityLog.copyWith(hasChanged: value);

  List<CaptionedPhoto> photos;

  factory StudentDailyReport.initialFromStudent(Student student) 
    return StudentDailyReport(
        student: student,
        moodController: TextEditingController(),
        healthEntries: 0: HealthEntry.empty(),
        supplies: Supplies(),
        pottyLog: PottyLog.empty(),
        activityLog: ActivityLog());
  

  StudentDailyReport.fromDocument(DocumentSnapshot document)
      : documentReference = document.reference 
    Map json = document.data;
    // Ensure that the student has been converted and inserted as a student object
    // into the json before calling.
    this.student = json['student'];
    this.mood = json['mood'] != null ? Mood.fromJson(json['mood']) : Mood();
    this.attendanceStatus = json['attendanceStatus'] ?? AttendanceStatus.none;
    this.checkedInTime = json['checkedInTime'] != null
        ? (json['checkedInTime'] as Timestamp).toDate()
        : null;
    this.checkedOutTime = json['checkedOutTime'] != null
        ? (json['checkedOutTime'] as Timestamp).toDate()
        : null;
    this.supplies = json['supplies'] != null
        ? Supplies.fromJson(json['supplies'])
        : Supplies();
    this.note = json['note'] != null ? Note.fromJson(json['note']) : Note();
    noteController = TextEditingController(text: note.comment);
    this.mealRecords = MealRecord.mapFromJsonList(json['mealRecords']);
    this.healthEntries = json['healthEntries'] != null
        ? (json['healthEntries'] as List)
            .map((json) => HealthEntry.fromJson(json))
            .toList()
            .asMap()
        : 0: HealthEntry.empty();
    this.pottyLog = json["pottyLog"] != null
        ? PottyLog.fromJson(json["pottyLog"])
        : PottyLog();
    this.activityLog = json["activityLog"] != null
        ? ActivityLog.fromJson(json["activityLog"])
        : ActivityLog();
    this.photos = json['photos'] != null
        ? (json['photos'] as List)
            .map((photoJson) => CaptionedPhoto.fromJson(photoJson))
            .toList()
        : List<CaptionedPhoto>();
    this.checkInChanged = false;
    this.checkOutChanged = false;
    this.healthChanged = false;
    this.mealsChanged = false;
    this.noteChanged = false;
    this.moodHasChanged = false;
    this.moodController = TextEditingController();
  

  bool validateMeals() 
    return mealRecords.values.any((mealRecord) => !mealRecord.isValid);
  

  /// Used to indicate when changes need to be saved.
  bool get hasUnsavedChanges 
    bool result;
    if (moodHasChanged == null ||
        checkInChanged == null ||
        supplies.suppliesHasChanged == null ||
        checkOutChanged == null ||
        noteChanged == null ||
        checkOutChanged == null ||
        mealsChanged == null ||
        healthChanged == null ||
        pottyLogChanged == null ||
        activityLogChanged == null) 
      debugPrint("One of these is null!");
    
    try 
      result = moodHasChanged ||
          checkInChanged ||
          supplies.suppliesHasChanged ||
          checkOutChanged ||
          noteChanged ||
          checkOutChanged ||
          mealsChanged ||
          healthChanged ||
          pottyLogChanged ||
          activityLogChanged;
     on Exception 
      return false;
    

    return result;
  

  StudentDailyReport(
    this.documentReference,
    this.student,
    this.date,
    // Mood
    this.mood = const Mood(),
    this.moodHasChanged = false,
    this.moodController,

    // Check In
    this.attendanceStatus = AttendanceStatus.none,
    this.checkInChanged = false,
    this.checkedInTime,

    //Check Out
    this.checkOutChanged = false,
    this.checkedOutTime,

    // Supplies
    this.supplies,
    this.mealRecords = const ,
    this.mealsChanged = false,

    // Notes
    this.noteController,
    this.note = const Note.empty(),
    this.noteChanged = false,

    // Health
    this.healthEntries,
    this.healthChanged = false,

    //PottyLog
    this.pottyLog,

    //ActivityLog
    this.activityLog,
    this.photos,
  ) 
    // assertions
    if (attendanceStatus == AttendanceStatus.present) 
      assert(checkedInTime != null);
    
    if (attendanceStatus == AttendanceStatus.checkedOut) 
      assert(checkedOutTime != null);
    
    this.date ??= DateTime.now();
    this.noteController = TextEditingController();
    this.moodController = TextEditingController();
  

  StudentDailyReport copyWith(
    DocumentReference documentReference,
    Mood mood,
    bool moodChanged,
    bool checkInChanged,
    DateTime checkedInTime,
    bool checkedOut,
    DateTime checkedOutTime,
    bool checkOutChanged,
    TextEditingController moodController,
    String attendanceStatus,
    Note note,
    Supplies supplies,
    TextEditingController notesController,
    bool notesChanged,
    TextEditingController suppliesController,
    bool suppliesChanged,
    bool hasUnsavedChanges,
    Map<Meal, MealRecord> mealRecords,
    bool mealsChanged,
    Map<int, HealthEntry> todaysHealthEntries,
    bool healthChanged,
    PottyLog pottyLog,
    bool pottyLogChanged,
    ActivityLog activityLog,
    bool activityLogChanged,
    List<CaptionedPhoto> photos,
    DateTime date,
  ) 
    return StudentDailyReport(
        documentReference: documentReference ?? this.documentReference,
        student: this.student,
        mood: mood ?? this.mood,
        moodHasChanged: moodChanged ?? this.moodHasChanged,
        checkInChanged: checkInChanged ?? this.checkInChanged,
        attendanceStatus: attendanceStatus ?? this.attendanceStatus,
        checkedInTime: (attendanceStatus == AttendanceStatus.present &&
                checkedInTime == null)
            ? DateTime.now()
            : checkedInTime ?? this.checkedInTime,
        moodController: this.moodController,
        note: note ?? this.note,
        noteController: this.noteController,
        noteChanged: noteChanged ?? this.noteChanged,
        supplies: supplies ?? this.supplies,
        checkOutChanged: checkOutChanged ?? this.checkOutChanged,
        checkedOutTime: attendanceStatus == AttendanceStatus.present &&
                checkedOutTime == null
            ? DateTime.now()
            : this.checkedOutTime,
        mealRecords: mealRecords ?? this.mealRecords,
        mealsChanged: mealsChanged ?? this.mealsChanged,
        healthEntries: todaysHealthEntries ?? this.healthEntries,
        healthChanged: healthChanged ?? this.healthChanged,
        pottyLog: () 
          PottyLog newPottyLog = pottyLog ??= this.pottyLog;
          if (pottyLogChanged == false) 
            return newPottyLog.copyWith(hasChanged: false);
          
          return newPottyLog;
        (),
        activityLog: activityLog ?? this.activityLog,
        photos: photos ?? this.photos,
        date: date ?? this.date);
  

  String get fullName => student.fullName;

  Map<String, dynamic> toJson() 
    Map<String, dynamic> data = ;

    if (this.mood != null && this.mood.mood != "" && this.moodHasChanged) 
      data["mood"] = this.mood.toJson();
    

    // attendance
    if (this.checkInChanged || this.checkOutChanged) 
      data["attendanceStatus"] = this.attendanceStatus;
    
    if (this.checkInChanged) 
      assert(checkedInTime != null);
      data['checkedInTime'] = Timestamp.fromDate(this.checkedInTime);
    
    if (this.checkOutChanged) 
      assert(checkedOutTime != null);
      data['checkedOutTime'] = Timestamp.fromDate(this.checkedOutTime);
    
    if (this.supplies.suppliesHasChanged) 
      data["supplies"] = this.supplies.toJson();
    
    if (this.mealsChanged) 
      data['mealRecords'] = this
          .mealRecords
          .values
          .map((mealRecord) => mealRecord.toJson())
          .toList();
    
    if (this.noteChanged) 
      data['note'] = this.note.toJson();
    
    data['student'] = this.student.documentReference;
    if (healthChanged) 
      data['healthEntries'] = healthEntries.values
          .map((healthEntry) => healthEntry.toJson())
          .toList();
    
    if (this.pottyLogChanged) 
      data['pottyLog'] = this.pottyLog.toJson();
    
    if (this.activityLogChanged) 
      data['activityLog'] = this.activityLog.toJson();
    
    return data;
  

  StudentDailyReport updateWith(DocumentSnapshot documentChange) 
    Map json = documentChange.data;

    return StudentDailyReport(
        documentReference: documentChange.reference,
        student: student,
        mood: json['mood'] != null ? Mood.fromJson(json['mood']) : mood,
        attendanceStatus: json['attendanceStatus'] ?? attendanceStatus,
        checkedInTime:
            (json['checkedInTime'] as Timestamp)?.toDate() ?? checkedInTime,
        checkedOutTime:
            (json['checkedOutTime'] as Timestamp)?.toDate() ?? checkedOutTime,
        supplies: json['supplies'] != null
            ? Supplies.fromJson(json['supplies'])
            : supplies,
        note: json['note'] != null ? Note.fromJson(json['note']) : note,
        mealRecords: MealRecord.mapFromJsonList(json['mealRecords']),
        healthEntries: json['healthEntries'] != null
            ? (json['healthEntries'] as List)
                .map((json) => HealthEntry.fromJson(json))
                .toList()
                .asMap()
            : healthEntries,
        pottyLog: json["pottyLog"] != null
            ? PottyLog.fromJson(json["pottyLog"])
            : pottyLog,
        activityLog: json["activityLog"] != null
            ? ActivityLog.fromJson(json["activityLog"])
            : activityLog,
        photos: json['photos'] != null
            ? (json['photos'] as List)
                .map((json) => CaptionedPhoto.fromJson(json))
                .toList()
            : []);
  

  @override
  String toString() 
    return "Report for $fullName";
  

【问题讨论】:

你能粘贴你想要序列化的类的源代码吗? 当然,我现在添加到原始帖子中。它包含对变量中其他类的引用 【参考方案1】:

目前我们不支持地图中的“复杂”键——例如Meal

为这个问题加注星标 - https://github.com/dart-lang/json_serializable/issues/396

【讨论】:

哦,我现在明白了。谢谢你。并且会做

以上是关于在 Dart 中使用 json_serializer 将映射序列化为 JSON 的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

将 Django 对象传递给 ExtJS 的正确方法

dart系列之:在dart中使用生成器

#yyds干货盘点#dart系列之:在dart中使用生成器

如何在 Dart 中使用类型别名/类型定义(也是非函数)?

在 Dart 中何时使用 mixins 以及何时使用接口?

如何在单元测试中访问 Dart 类