6

I'm trying to generate my json helper functions for an object, that contains a list with the type of an abstract class, like this:

import 'package:json_annotation/json_annotation.dart';

import 'exercise-variations.a.dart';

part 'routine.model.g.dart';

@JsonSerializable()
class Routine {

  List<ExerciseRoutine> exercises;

  Routine();

  factory Routine.fromJson(Map<String, dynamic> json) => _$RoutineFromJson(json);

  Map<String, dynamic> toJson() => _$RoutineToJson(this);
}


    import 'package:json_annotation/json_annotation.dart';
    import 'exercise-variations.a.dart';

    part 'base-exercise-routine.g.dart';

    @JsonSerializable()
    class BaseExerciseRoutine implements ExerciseRoutine {
      int sets;

      BaseExerciseRoutine();

      factory BaseExerciseRoutine.fromJson(Map<String, dynamic> json) => _$BaseExerciseRoutineFromJson(json);

      Map<String, dynamic> toJson() => _$BaseExerciseRoutineToJson(this);
    }

abstract class ExerciseRoutine {}

This way I get this error:

[INFO] Running build...
[SEVERE] json_serializable:json_serializable on lib/test/routine.model.dart:
Error running JsonSerializableGenerator
Could not generate `fromJson` code for `exercises` because of type `ExerciseRoutine`.
None of the provided `TypeHelper` instances support the defined type.
package:dojohub_app_flutter/test/routine.model.dart:11:25
   ╷
11 │   List<ExerciseRoutine> exercises;
   │                         ^^^^^^^^^
   ╵
[INFO] Running build completed, took 1.2s

[INFO] Caching finalized dependency graph...
[INFO] Caching finalized dependency graph completed, took 92ms

[SEVERE] Failed after 1.3s
pub finished with exit code 1

Which makes sense, because ExerciseRoutine does not implement a toJson function and a fromJson factory. Maybe I could add a toJson function to my abstract class but how do I fix the missing fromJson factory?

Pi Da
  • 359
  • 2
  • 8
Jonas
  • 7,089
  • 15
  • 49
  • 110

2 Answers2

9

I solved it by writing a custom fromJson and toJson function for the exercises Field

part 'routine.model.g.dart';

@JsonSerializable()
class Routine extends WorkoutRoutine {

  @JsonKey(fromJson: exercisesFromJson, toJson: exercisesToJson)
  List<ExerciseRoutine> exercises;

  Routine();

  factory Routine.fromJson(Map<String, dynamic> json) => _$RoutineFromJson(json);

  Map<String, dynamic> toJson() => _$RoutineToJson(this);
}

List<ExerciseRoutine> exercisesFromJson(List<dynamic> json) {
  return json.map((e) {
    return BaseExerciseRoutine.fromJson(e);
  }).toList();
}

List<dynamic> exercisesToJson(List<ExerciseRoutine> exercises) {
  return exercises.map((e) {
    switch (exercises.runtimeType) {
      case BaseExerciseRoutine:
        return (e as BaseExerciseRoutine).toJson();
      default:
        return null;
        break;
    }
  }).toList();
}
Jonas
  • 7,089
  • 15
  • 49
  • 110
0

In my project I had the similar issue, so I solved it this way:

Base class:

import 'package:json_annotation/json_annotation.dart';
import 'package:katharsis/core/api_services/api_date_formatter.dart';

enum MessageStatus { notSend, notViewed, viewed }

abstract class Message {
  final int id;
  final int userId;
  @JsonKey(name: 'date', fromJson: dateFromJson)
  final DateTime date;
  @JsonKey(name: 'out', fromJson: meFromJson)
  final bool me;
  final String? text;

  const Message({
    required this.id,
    required this.userId,
    required this.date,
    required this.me,
    this.text,
  });

  static bool meFromJson(int json) => json == 1;
  static DateTime dateFromJson(int json) =>
      ApiDateFormatter.convertFromTimestamp(json)!;
}


Inherited class:

import 'package:json_annotation/json_annotation.dart';
import 
part 'text_message.g.dart';

@JsonSerializable(fieldRename: FieldRename.snake)
class TextMessage extends Message {
  const TextMessage({
    required super.text,
    required super.me,
    required super.id,
    required super.userId,
    required super.date,
  });
}