Dart (Flutter) serialize nested generics
Tags: flutter, dart, serializer
Published: 2020-02-07 15:07:32

---
title: Dart (Flutter) serialize nested generics
published: true
description: 
tags: flutter, dart, serializer
---

Few days ago, we’ve had to serialize a paged results response.

This PagedResult object has a List of Generic results, so depending on what result we want to receive, we need to map as one or othe Class.

## The Trouble

Flutter doesn’t supports Reflection and neither allow to pass class or constructor as function parameter.

## Model examples

```dart
class PagedResult<T> {
  final int totalItems;
  final int startItems;
  final int itemsPerPage;
  final int currentPage;
  final int totalPages;
  final List<T> results;

  PagedResult({
  this.totalItems,
  this.startItems,
  this.itemsPerPage,
  this.currentPage,
  this.totalPages,
  this.results});
}

```

```dart

@JsonSerializable()
class Customer {
  final int id;
  final String firstName;
  final String lastName;
  final String secondLastName;
  final String phone1;
  final String phone2;
  final String email;

  Customer(
      {this.id,
      this.firstName,
      this.lastName,
      this.secondLastName,
      this.phone1,
      this.phone2,
      this.email});

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

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


```dart

@JsonSerializable()
class Product {
  final int id;
  final String ref;
  final double price;
  final String description;
  final double tax;

  Product(
      {this.id,
      this.ref,
      this.price,
      this.description,
      this.tax});

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

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

Our goal is to serialize PagedResults as PagedResults of Customer and PagedResults of Product.

We have multiple options to do it:

### Brute force

We can create one PagedResult class for each "class":

```dart

class PagedResultCustomer<Customer> {
  final int totalItems;
  final int startItems;
  final int itemsPerPage;
  final int currentPage;
  final int totalPages;
  final List<Customer> results;

  PagedResult({
  this.totalItems,
  this.startItems,
  this.itemsPerPage,
  this.currentPage,
  this.totalPages,
  this.results});

 factory PagedResultCustomer.fromJson(Map<String, dynamic> json) {
   final items = json['results'].cast<Map<String, dynamic>>();
   return PagedResult<Customer>(
    totalItems = json.totalItems;
     .
     .
     .
    results = new List<Customer>.from(items.map((itemsJson) => Customer.fromJson(itemsJson)))
    )
 }
}

class PagedResultProduct<Product> {
  final int totalItems;
  final int startItems;
  final int itemsPerPage;
  final int currentPage;
  final int totalPages;
  final List<Product> results;

  PagedResult({
  this.totalItems,
  this.startItems,
  this.itemsPerPage,
  this.currentPage,
  this.totalPages,
  this.results});

factory PagedResultProduct.fromJson(Map<String, dynamic> json) {
  final items = json['results'].cast<Map<String, dynamic>>();  
  return PagedResult<Product>(
    totalItems = json.totalItems;
     .
     .
     .
    results = new List<Product>.from(items.map((itemsJson) => Product.fromJson(itemsJson)))
    )
}

```

It's a simple solution but not accomplish with [DRY PRINCIPLE](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)

### Young Padawan

Another way is try to follow DRY PRINCIPLE and based on code above we can do:
```dart

class PagedResult<T> {
  final int totalItems;
  final int startItems;
  final int itemsPerPage;
  final int currentPage;
  final int totalPages;
  final List<T> results;

  PagedResult({
  this.totalItems,
  this.startItems,
  this.itemsPerPage,
  this.currentPage,
  this.totalPages,
  this.results});

 /// We require a second parameter to be an object that implements a 
/// fromJson method. Ok, I'm not using Typing for this, but if object
/// hasn't fromJson method, we'll throw a new Exception :)
factory PagedResult.fromJson(Map<String, dynamic> json, object) {
  final items = json['results'].cast<Map<String, dynamic>>(); 
  return PagedResult<T>(
    totalItems = json.totalItems;
     .
     .
     .
    results = new List<T>.from(items.map((itemsJson) => object.fromJson(itemsJson)))
    )
 }
}

@JsonSerializable()
class Customer {
  final int id;
  final String firstName;
  final String lastName;
  final String secondLastName;
  final String phone1;
  final String phone2;
  final String email;

  Customer(
      {this.id,
      this.firstName,
      this.lastName,
      this.secondLastName,
      this.phone1,
      this.phone2,
      this.email});

/// We don't use factory, instead we implement a fromJson method object
Customer fromJson(Map<String, dynamic> json) => _$CustomerFromJson(json);

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

To run: 

```dart
/// We pass a new object Customer as parameter
final PagedResult<Customer> pagedCustomer = PagedResult<Customer>.fromJson(json, new Customer());

```

Ok, it's a better solution than _Brute Force_ but I don't like the requirement to need to instantiate a new object for this.


### Jedi

Dart doesn't support Reflection, or pass class/factory as parameter, but it allow to pass a method :)

So we can change a bit the code above:

```dart

class PagedResult<T> {
  final int totalItems;
  final int startItems;
  final int itemsPerPage;
  final int currentPage;
  final int totalPages;
  final List<T> results;

  PagedResult({
  this.totalItems,
  this.startItems,
  this.itemsPerPage,
  this.currentPage,
  this.totalPages,
  this.results});

 /// We require a second parameter to be a Function, and we'll trigger 
/// this function for serializing results property
 factory PagedResult.fromJson(Map<String, dynamic> json, Function fromJson) {
   final items = json['results'].cast<Map<String, dynamic>>();
   return PagedResult<T>(
    totalItems = json.totalItems;
     .
     .
     .
    results = new List<T>.from(items.map((itemsJson) => fromJson(itemsJson)))
    )
 }
}

@JsonSerializable()
class Customer {
  final int id;
  final String firstName;
  final String lastName;
  final String secondLastName;
  final String phone1;
  final String phone2;
  final String email;

  Customer(
      {this.id,
      this.firstName,
      this.lastName,
      this.secondLastName,
      this.phone1,
      this.phone2,
      this.email});

/// We change object method to a static class method (avoid instantiate object)
static Customer fromJson(Map<String, dynamic> json) => _$CustomerFromJson(json);

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

To run: 

```dart
/// We pass a new object Customer as parameter
final PagedResult<Customer> pagedCustomer = PagedResult<Customer>.fromJson(json, Customer.fromJson);

```

Of course, there are more solutions to serialize generics but I think the _Jedi_ method is a fine and clear solution to do it.