Search code examples
flutterdartobjectserialization

Dart: using named constructor on generic type in class template for deserializing multiple classes using a generic interface


I want to create several objects that are serializable to a Map and deserializable from a Map so I can deserialize an entire List of such objects in other functions and methods. This is meant to avoid having to define a custom method for deserializing a List of objects for each type but instead being able to use a single one.

abstract class Serializable {
  Map<String, dynamic> toMap();
  Serializable.fromMap(Map<String, dynamic> map);
}

class Schema<T extends Serializable> {
  List<T> data = [];

  List<Map<String, dynamic>> serialize() {
    return data.map((e) => e.toMap()).toList(); // works fine
  }

  void deserialize(List<Map<String, dynamic>> data) {
    this.data = data.map((e) => T.fromMap(e)).toList(); // fromMap is not defined for type 'Type'
  }
}

This would be useful for example to store multiple objects of a class in a JSON file. As that storing part gets a bit complicated as it includes encryption I want to have a generic interface for serialization and deserialization.

class User extends Serializable {
  String username;
  
  @override
  Map<String, dynamic> toMap() {
    return { 'username': username};
  }

  @override
  User.fromMap(Map<String, dynamic> map) :
    username = map['username'],
}

Schema<User> users = Schema<User>();
users.deserialize(somemap);

However I cannot come up with a working solution for deserialization. If I use a generic type (that extends Serializable) I cannot access the named constructor. Defining fromMap as static doesn't work as well as static methods as you can't have abstract static methods.

Is there another way for achieving multiple classes that are deserializable using a generic interface?


Solution

  • I solve this based on this post about Creating an instance of a generic type in DART.

    typedef ItemCreator<S> = S Function(Map<String, dynamic> map);
    
    class Schema<T extends Serializable> {
      List<T> data = [];
      ItemCreator<T> fromMap;
    
      Schema(this.fromMap);
    
      void deserialize(List<Map<String, dynamic>> data) {
        this.data = data.map((e) => fromMap(e)).toList();
      }
    }
    

    In this solution when constructing Schema the .fromMap constructor is passed.

    Schema<User> users = Schema<User>(User.fromMap);