@encoder / @decoder

Setup encoding and decoding for custom data types.

With the @encoder and @decoder annotations you can set up custom serialization for any type. This is used by Jaspr for both:

  • (de-)serializing parameters of @client components
  • (de-)serializing fields using the @sync annotation

Usage

To enable custom serialization for your data class use the annotations in the following way:

  • The @decoder annotation must be used on a constructor or static method that returns an instance of the class and has exactly one parameter of any primitive serializable type (bool, int, String, etc.).

  • The @encoder annotation must be used on an instance method that returns the same type that the @decoder method takes in.

  • Both methods can have any name.

In practice this can look like this:

model.dart
class Model {
  // Constructors, fields, etc.
  /* ... */

  @decoder
  static Model decode(Map<String, dynamic> data) => /* ... */;

  @encoder
  Map<String, dynamic> encode() => /* ... */;
}

The annotations are designed to be compatible with virtually any serialization package like json_serializable or dart_mappable. Simply annotate the methods generated by these packages either directly, or by using a wrapper method.

While Map<String, dynamic> is the most common type for serializing dart classes, you can also use other primitive types like String, List<dynamic> etc. as long as the encoding method returns the same type as the decoding method accepts.

Customize serialization of third-party types

Sometimes, you cannot control the methods of a class, such as when using a third-party library. In these cases, you can create a custom override type which "redefines" the type for serialization.

For example, consider the case where the Model class is imported from a third-party library, package:model. Since we do not own package:model, we cannot change the methods of the Model class.

Instead of modifying the Model class, we can define a custom override for Model which will be used in place of the original Model class when it's used for serialization in Jaspr.

lib/model_override.dart
import 'package:jaspr/jaspr.dart';
import 'package:model/model.dart';

// We create an extension type which wraps over the third-party type and
// defines the @decoder and @encoder methods
//
// Since the extension type implements the original type, it will behave
// identically to the original except for the serialization logic.
extension type ModelOverride(Model model) implements Model {
  @decoder
  factory ModelOverride.decode(Map<String, dynamic> data) {
    return ModelOverride(
      Model(
        // decode the values of [data]
        /* ... */
      ),
    );
  }

  @encoder
  Map<String, dynamic> encode() => {
      // encode the values of [model]
      /* ... */
    };
}

Custom overrides are defined as extension types which wrap and implement the original type. You must always implement the original type in your extension type.