Home > front end >  How can I create a generic implementation of fromJson and toJson in Dart?
How can I create a generic implementation of fromJson and toJson in Dart?

Time:01-10

I am attempting to implement an inheritance pattern that allows for creating instances of an extended class using generic types. The below code appears to be a working pattern, yet the compiler complains that T.fromJson() is not implemented. Specifically, it gets red underlined squiggles in VS Code with the following error:

The method 'fromJson' isn't defined for the type 'Type'. Try correcting the name to the name of an existing method, or defining a method named 'fromJson'.

I am encountering this during a refactor effort but was able to create a semi-simplified example of the pattern I think should work:

class TestClass{
  TestClass();

  factory TestClass.fromJson( Map<String, dynamic> json ) => TestClass();
}

class ATest extends TestClass {
  ATest();

  @override
  factory ATest.fromJson( Map<String, dynamic> json ) => ATest();
}

class BTest extends TestClass {
  BTest();

  @override
  factory BTest.fromJson( Map<String, dynamic> json ) => BTest();
}

class CTest extends TestClass {
  CTest();

  @override
  factory CTest.fromJson( Map<String, dynamic> json ) => CTest();
}

class FactoryController<T extends TestClass>{
  Future<List<T>> listNetworkObjects<T extends TestClass>({
    required  List<String>  filters,
  }) async {
    try {
      final data = await MockApi().mockApiCall<T>( filters );
      final List<T> allItems = List<T>.from(
        data.map( ( T model ) => T.fromJson( model ) )
      );

      if ( allItems.isNotEmpty ) {
        print( 'Received Items from the API.' );
      }
      return allItems;
    }
    catch ( getTestClassError, stackTrace ) {
      print( 'Error getting TestClasses' );
      print( stackTrace );
      return <T>[];
    }
  }
}

class MockApi{
  MockApi();

  dynamic mockApiCall<T extends TestClass>(
    List<String> filters
  ) async {
    final dynamic data = { "test": "object" };
    return data;
  }
}

I would expect that the method listNetworkObjects would provide visibility to the compiler that the base class and all implementations of it also provide an implementation of the .fromJson() method of the class.

Instead of data.map( ( T model ) => T.fromJson( model ) ) I have tried:

data.map( ( T model ) => T().fromJson( model ) )

data.map( ( T model ) => new T.fromJson( model ) )

data.map( ( T model ) => new T().fromJson( model ) )

data.map( ( model as T ) => T.fromJson( model ) )

data.map( ( T model ) => T.new.fromJson( model ) )

each of which fails for a different reason.

I see other similar questions about abstract class constructors etc. like Flutter abstract class with factory methods but my base class is not abstract. I also tried with using implements instead of extends but:

listNetworkObjects<T implements TestClass>()

is not a valid method or class specification. I feel like there could be an answer here but I can't figure out how to implement it properly: Creating an instance of a generic type in DART.

Thanks!

CodePudding user response:

It is a limitation of Dart that static methods of generic types cannot be called (including constructors and factories).

In some situations I've used a pattern like below, with the disadvantage that it requires an instantiated object of the type to be passed in:

abstract class TestClass{
  TestClass fromJson(Map<String, dynamic> json);
}

class Foo<T extends TestClass> {
  T someMethod(T proto) => proto.fromJson(...) as T;
}
  • Related