schultek / dart_mappable

Improved json serialization and data classes with full support for generics, inheritance, customization and more.
https://pub.dev/packages/dart_mappable
MIT License
164 stars 23 forks source link
dart hacktoberfest

dart_mappable


Quickstart β€’ Documentation β€’ Example β€’ Package Comparison

Improved json serialization and data classes with
full support for generics, inheritance, customization and more.


dart_mappable covers all basic features (from/to json, == override, hashCode, toString(), copyWith) while adding new or improved support for advances use-cases including generics, inheritance and polymorphism, customization and more.

Quick Start

First, add dart_mappable as a dependency, together with dart_mappable_builder and build_runner as a dev_dependency.

flutter pub add dart_mappable
flutter pub add build_runner --dev
flutter pub add dart_mappable_builder --dev

Next annotate your classes that you want to use with @MappableClass() and add the appropriate part directive to include the generated .mapper.dart file:

// This file is "model.dart"
import 'package:dart_mappable/dart_mappable.dart';

// Will be generated by dart_mappable
part 'model.mapper.dart';

@MappableClass()
class MyClass with MyClassMappable {
  final int myValue;

  MyClass(this.myValue);
}

To use a class you must:

Tip: Don't worry if the mixin don't exist at first, just run code-generation once an it will be created. The builder will also warn you if you define your class without the proper mixin.

Note: For generic classes (e.g. MyClass<T>) make sure to also provide all type parameters to the mixin (... with MyClassMappable<T>).


In order to generate the serialization code, run the following command:

dart pub run build_runner build

Tip: You'll need to re-run code generation each time you are making changes to your annotated classes. During development, you can use watch to automatically watch your changes: dart pub run build_runner watch.

This will generate a <filename>.mapper.dart file for each of your files containing annotated classes.


Last step is to use the generated mappers. There are two main ways to interact with your models using this package:

  1. Through the generated <ClassName>Mapper classes, and
  2. through the methods defined by the generated mixin.
...

void main() {
  // Decode a [Map] using the [MyClassMapper] class:
  MyClass myClass = MyClassMapper.fromMap({'myValue': 123});

  // Or decode directly from json:
  MyClass myClass2 = MyClassMapper.fromJson('{"myValue": 123}');

  // Encode an instance of your class using the methods provided by the mixin:
  Map<String, dynamic> map = myClass.toMap();
  String json = myClass.toJson();

  // There are also implementations generated for [operator ==], [hashCode] and [toString]:
  bool thisIsTrue = (myClass == myClass2);
  print(myClass);

  // Last you can use [copyWith] to create a copy of an object:
  MyClass myClass3 = myClass.copyWith(myValue: 0);
}

Beware: The .toJson() method returns a String. If you are migrating from json_serializable, you might be used to this returning a Map<String, dynamic> instead. Make sure to properly adapt your code to this change, as not doing so might lead to unexpected behavior. Find more about the recommended migration path here.

Overview

To setup, annotate your model classes with @MappableClass() and your enums with @MappableEnum(). Each annotation has a set of properties to configure the generated code.

@MappableClass()
class MyClass with MyClassMappable { ... }

@MappableEnum()
enum MyEnum { ... }

Tip: Check out the documentation about Models and Enums.

For deserialization, dart_mappable will use the first available constructor of a class, but you can use a specific constructor using the @MappableConstructor() annotation.

@MappableClass()
class MyClass with MyClassMappable {
  MyClass(); // Don't use this

  @MappableConstructor()
  MyClass.special(); // Use this
}

You can also annotate a single field or constructor parameter of a class using @MappableField() to set a specific json key or add custom hooks.

@MappableClass()
class MyClass with MyClassMappable {
  MyClass(this.value);

  @MappableField(key: 'my_key')
  String value;
}

Note: This can only be used on a field if it is directly assigned as a constructor parameter (MyClass(this.myField)). Setting this annotation on any other field will have no effect. (Read Utilizing Constructors for an explanation why this is.)

Tip: Hooks are a way to customize the serialization of any field or class. Read more in the documentation about Mapping Hooks.

You can add the @MappableLib() annotation to your library statement to set a default configuration for all included classes and enums, e.g. the case style for json keys.

@MappableLib(caseStyle: CaseStyle.camelCase) // will be applied to all classes
library model;

part 'model.mapper.dart';

@MappableClass() // no need to set caseStyle here
class MyClass with MyClassMappable {
  ...
}

Tip: Check out the documentation to see all available Configuration options.


Here are again all six annotations that you can use in your code:

  1. @MappableClass() can be used on a class to specify options like the caseStyle of the json keys, whether to ignore null values, or hooks.
  2. @MappableConstructor() can be used on a constructor to mark this to be used for decoding. It has no properties.
  3. @MappableField() can be used on a constructor parameter or a field to specify a json key to be used instead of the field name, or hooks.
  4. @MappableEnum() can be used on an enum to specify the mode or caseStyle of the encoded enum values, or the defaultValue.
  5. @MappableValue() can be used on an enum value to specify a custom encoded value to use.
  6. @MappableLib() can be used on a library statement or import / export statement to set a default configuration for the annotated library or include / exclude classes.

Mapper Interface

dart_mappable will generate Mapper classes that provide these methods or properties:

Tip: If you prefer to use MyClass.fromJson over MyClassMapper.fromJson, add the fromJson and fromMap methods directly to your class like this:

class MyClass with MyClassMappable {
  ...

 static final fromMap = MyClassMapper.fromMap;
 static final fromJson = MyClassMapper.fromJson;
}

The generated <ClassName>Mappable mixin will come with the following methods:

Full Documentation

See the full documentation here or jump directly to the topic you are looking for: