---
title: "@encoder / @decoder"
description: 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](/api/utils/at_client) components
- (de-)serializing fields using the [@sync](/api/utils/at_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:

```dart title="model.dart"
class Model {
  // Constructors, fields, etc.
  /* ... */

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

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

<Info>
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.
</Info>

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.

```dart title="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](https://dart.dev/language/extension-types) which wrap and implement
the original type. You must always implement the original type in your extension type.
