Flutter地图数据映射生成插件map_mapper_generator的使用

Flutter地图数据映射生成插件map_mapper_generator的使用

map_mapper_generator

该包及其配套的map_mapper_annotations包旨在显著减少将Dart业务模型类暴露给NoSQL数据库存储库的工作量。这些存储库以Map<String, dynamic>形式持久化数据,而不是强类型对象。

该项目深受json_serializable包的启发。然而,虽然序列化逻辑相对相似,但其暴露方式有所不同。json_serializable包生成私有方法,而本包则生成MapMapper<TEntity>的子类,该类包含一个将实例转换为Map<String, dynamic>的方法(Map<String, dynamic> toMap(TEntity instance))和一个从Map<String, dynamic>恢复实体的方法(TEntity fromMap(Map<String, dynamic>) map)。

这种做法的目的是能够开发通用的服务处理代码,该代码能够处理任何类型的 TEntity,将其序列化并发送到数据库,或从数据库检索并反序列化。

具体来说,通过这种方法,更容易开发一个处理gRPC请求的通用混入(mixin),并处理典型的CRUD请求。

此外,生成的代码还定义了类扩展,使得在序列化的类中无需创建工厂构造函数。

例如,如果我们有一个 Recipe 类,只需添加 @mapMap 属性即可使其可序列化,而无需添加其他方法或工厂:

[@mapMap](/user/mapMap)
class Recipe {
  final String key;
  final String title;
  final List<Ingredient> ingredients;

  Recipe({
      this.key = '',
      required this.title,
      required this.ingredients,
  });

  // 不需要在此处添加支持序列化的代码
}

相反,由于生成的代码为 Recipe 和 Map<String, dynamic> 添加了扩展,序列化和反序列化变得非常简单:

final recipe = Recipe(/* 初始化参数 */);

// 这将创建一个包含属性的 Map<String, dynamic>
// 注意我们实际上不需要在 recipe 中添加 toMap() 方法,
// 因为在 recipe.g.dart 中已经创建了一个该方法的扩展。
final recipeMap = recipe.toMap();

// 这将重新创建一个 Recipe。
// 这也是可能的,因为我们还为 Map<String, dynamic> 添加了一个扩展,
// 该扩展添加了名为 toRecipe() 的方法。
final recipe2 = recipeMap.toRecipe();

多态性

多态性允许我们将超类类型的变量引用子类,并以这样的方式序列化对象,使序列化的Map包含所有子类属性以及类型标识符。这使得我们能够在仅事先知道超类类型的情况下反序列化子类对象。该包支持多态性,但有一个限制:所有支持的子类类型必须在生成时已知,并且用 MapMapped() 注解的 knownSubClasses 属性注册。

以下是一个示例:

@mapMapped
class Car extends Vehicle {
  Car({
    required this.numberOfDoors,
    required super.weight,
  });

  final int numberOfDoors;
}

@mapMapped
class Airplane extends Vehicle {
  Airplane({
    required super.weight,
    required this.wingspan,
  });

  final int wingspan;
}

// 注意我们正在识别所有已知的子类
@MapMapped(knownSubClasses: [
  Car,
  Airplane,
])
abstract class Vehicle { // 可以是非抽象的
  final int weight;
  Vehicle({
    required this.weight,
  });
}

void main() {
  final car = Car(numberOfDoors: 4, weight: 1500);
  final Vehicle airplane = Airplane(wingspan: 13, weight: 1500);

  final vehicles = <Vehicle>[car, airplane];
  // 从此处我们可以得到一个包含每个 Vehicle 序列化的列表
  final maps = vehicles.map((v) => v.toMap()).toList();

  // 注意我们调用了 .toVehicle,因为我们知道它们都是车辆
  final deserVehicles = maps.map((m) => m.toVehicle()).toList();

  final deserCar = deserVehicles[0] as Car; // 它真的是一个汽车
  final deserAirplane = deserVehicles[1] as Airplane; // 它真的是一个飞机

  print(deserCar.numberOfDoors); // 应该打印 4
  print(deserAirplane.wingspan); // 应该打印 13
}

入门指南

要开始使用,请查看示例项目:

https://gitlab.com/dartaculous/dartaculous/-/tree/main/map_mapper/example

更多关于Flutter地图数据映射生成插件map_mapper_generator的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter地图数据映射生成插件map_mapper_generator的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


map_mapper_generator 是一个用于 Flutter 的代码生成插件,它可以帮助你将 Dart 类映射到地图(Map)数据结构,或者从地图数据结构生成 Dart 类。这个插件特别适用于需要将对象序列化为 JSON 或从 JSON 反序列化的场景。

安装

首先,你需要在 pubspec.yaml 文件中添加依赖:

dependencies:
  map_mapper_annotation: ^1.0.0

dev_dependencies:
  build_runner: ^2.1.0
  map_mapper_generator: ^1.0.0

使用步骤

  1. 定义模型类:使用 [@MapMapper](/user/MapMapper)() 注解来标记你的模型类。
import 'package:map_mapper_annotation/map_mapper_annotation.dart';

part 'user.g.dart';

[@MapMapper](/user/MapMapper)()
class User {
  final String name;
  final int age;
  final String email;

  User({
    required this.name,
    required this.age,
    required this.email,
  });
}
  1. 生成代码:使用 build_runner 生成映射代码。

在终端中运行以下命令:

flutter pub run build_runner build

这将会生成一个 user.g.dart 文件,其中包含了 User 类的映射逻辑。

  1. 使用生成的映射代码:你可以使用生成的 toMapfromMap 方法来序列化和反序列化对象。
import 'user.dart';
import 'user.g.dart';

void main() {
  var user = User(name: 'John Doe', age: 30, email: 'john.doe@example.com');

  // Serialize to Map
  var userMap = UserMapper.toMap(user);
  print(userMap); // Output: {name: John Doe, age: 30, email: john.doe@example.com}

  // Deserialize from Map
  var newUser = UserMapper.fromMap(userMap);
  print(newUser.name); // Output: John Doe
}

自定义映射

你可以通过 @MapField() 注解来自定义字段的映射方式。例如:

[@MapMapper](/user/MapMapper)()
class User {
  @MapField(name: 'full_name')
  final String name;

  final int age;

  @MapField(ignore: true)
  final String email;

  User({
    required this.name,
    required this.age,
    required this.email,
  });
}

在这个例子中,name 字段将被映射为 full_name,而 email 字段将被忽略。

处理嵌套对象

如果你的类中包含嵌套对象,map_mapper_generator 也可以处理这种情况。例如:

[@MapMapper](/user/MapMapper)()
class Address {
  final String city;
  final String country;

  Address({
    required this.city,
    required this.country,
  });
}

[@MapMapper](/user/MapMapper)()
class User {
  final String name;
  final int age;
  final Address address;

  User({
    required this.name,
    required this.age,
    required this.address,
  });
}
回到顶部