Flutter Dart Macro 宏简化 JSON 序列化

Flutter Dart Macro 宏简化 JSON 序列化

视频

https://youtu.be/gBZYjBmoOcU

https://www.bilibili.com/video/BV1tT421a7ev/

前言

原文 https://ducafecat.com/blog/using-dart-macros-to-simplify-json-serialization

今天我们将会体验 dart 语言新特性 macro 宏,来实现对 json 的序列化,用到的包是官方实验室写的 json 包。

本文将会一步步的带你实现这个功能,那我们开始吧。

Dart, 宏, JSON 序列化, 代码生成, 效率优化, 语言特性

参考

https://dart.dev/language/macros#view-the-generated-code

https://github.com/dart-lang/language/blob/main/working/macros/feature-specification.md

https://pub.dev/packages/json

macro 宏

好处

  • 直接语言集成。由于宏直接内置于语言中,它们比外部库提供更多的一致性并兼容语言的本机功能。
  • 减少外部依赖。通过使用宏来减少对外部库的依赖,您可以减小应用程序的大小并简化依赖管理。
  • 编译时代码生成。与运行时生成代码的解决方案相比,宏在编译时生成代码可能会提供更快的结果。
  • 增强定制化和自由度。开发者可以借助宏更好地根据项目独特需求定制代码生成,提供大量的定制自由度。

json_serializable 比较

为了在 Dart 中生成 JSON 序列化代码,这个库经常被使用,但它需要更多的依赖和设置。无需额外的工具,宏可以在源代码中自动生成 fromJson 和 toJson 方法。

freezed 比较

使用这个库,创建类型安全、不可变的 Dart 类变得非常简单。虽然 freezed 功能强大,但宏可能以更少的依赖和更直接的集成来完成类似的任务。

实现步骤

第一步:切到 dart 3.5 版本

https://docs.flutter.dev/release/archive?tab=macos

下载 beta channel , dart 3.5 版本。

我本机用的 fvm 管理多版本。

检查环境

❯ flutter --version

Flutter 3.23.0-0.1.pre • channel beta • https://github.com/flutter/flutter.git
Framework • revision 2feea7a407 (13 天前) • 2024-06-06 10:19:10 +0700
Engine • revision bb10c54666
Tools • Dart 3.5.0 (build 3.5.0-180.3.beta) • DevTools 2.36.0

第二步:配置 pubspec.yaml

pubspec.yaml

environment:
  sdk: ">=3.5.0-180.3.beta <4.0.0"
  
dependencies:
  json: ^0.20.2

官方实验室 json 包
https://pub.dev/packages/json

analysis_options.yaml

analyzer:
  enable-experiment:
    - macros

在编写代码时,除非你告诉分析器你正在试验此功能,否则它会发出警告。

第三步:编写测试代码

编写官方示例代码

lib/macros/user.dart

import 'package:json/json.dart';

@JsonCodable() // Macro annotation.
class User {
  final int? age;
  final String name;
  final String username;
}

可以点击 Go to Augmentation 查看详细

augment library 'package:flutter_application_macro/macros/user.dart';

import 'dart:core' as prefix0;

augment class User {
  external User.fromJson(prefix0.Map<prefix0.String, prefix0.Object?> json);
  external prefix0.Map<prefix0.String, prefix0.Object?> toJson();
  augment User.fromJson(prefix0.Map<prefix0.String, prefix0.Object?> json, )
      : this.age = json[r'age'] as prefix0.int?,
        this.name = json[r'name'] as prefix0.String,
        this.username = json[r'username'] as prefix0.String;
  augment prefix0.Map<prefix0.String, prefix0.Object?> toJson() {
    final json = <prefix0.String, prefix0.Object?>{};
    if (this.age != null) {
      json[r'age'] = this.age!;
    }
    json[r'name'] = this.name;
    json[r'username'] = this.username;
    return json;
  }
}

这段代码自动的,不需要你编写。

启动时加入测试 lib/main.dart

class _MyHomePageState extends State<MyHomePage> {

  @override
  void initState() {
    super.initState();

    // Given some arbitrary JSON:
    final userJson = {
      'age': 5,
      'name': 'Roger',
      'username': 'roger1337',
    };

    // Use the generated members:
    final user = User.fromJson(userJson);
    print(user);
    print(user.toJson());
  }
  ...

如果代码成功运行将会打印 json 字符串

最后:运行测试

加入参数 --enable-experiment=macros 执行

我是 vsc 所以加在了 .vscode/launch.json

  "configurations": [
    {
      "name": "flutter_application_macro",
      "request": "launch",
      "type": "dart",
      "flutterMode": "debug",
      "program": "lib/main.dart",
      "args": ["--enable-experiment=macros"]
    },

主要是在 args 中加入 --enable-experiment=macros 参数

最后我们启动,查看输出信息。

Launching lib/main.dart on iPhone 15 in debug mode...
Xcode build done.                                           11.3s
Connecting to VM Service at ws://127.0.0.1:55696/aP-7aqbeIA4=/ws
Connected to the VM Service.
flutter: Instance of 'User'
flutter: {age: 5, name: Roger, username: roger1337}

官方 json 实现

https://pub.dev/packages/json

代码

https://github.com/dart-lang/sdk/blob/main/pkg/json/lib/json.dart

macro class JsonCodable
    with _Shared, _FromJson, _ToJson
    implements ClassDeclarationsMacro, ClassDefinitionMacro {
  const JsonCodable();

  /// Declares the `fromJson` constructor and `toJson` method, but does not
  /// implement them.
  @override
  Future<void> buildDeclarationsForClass(
      ClassDeclaration clazz, MemberDeclarationBuilder builder) async {
    final mapStringObject = await _setup(clazz, builder);

    await (
      _declareFromJson(clazz, builder, mapStringObject),
      _declareToJson(clazz, builder, mapStringObject),
    ).wait;
  }

  /// Provides the actual definitions of the `fromJson` constructor and `toJson`
  /// method, which were declared in the previous phase.
  @override
  Future<void> buildDefinitionForClass(
      ClassDeclaration clazz, TypeDefinitionBuilder builder) async {
    final introspectionData =
        await _SharedIntrospectionData.build(builder, clazz);

    await (
      _buildFromJson(clazz, builder, introspectionData),
      _buildToJson(clazz, builder, introspectionData),
    ).wait;
  }
}
  1. buildDeclarationsForClass 方法:
    • 在类声明阶段,为该类生成 fromJson 构造函数和 toJson 方法的声明。
  2. buildDefinitionForClass 方法:
    • 在类定义阶段,为 fromJsontoJson 提供具体的实现逻辑。
    • 该实现逻辑是根据类的属性信息自动生成的,通过反射获取类的结构信息。

代码

https://github.com/ducafecat/flutter_develop_tips/tree/main/flutter_application_macro_json

小结

这个 macro 实现的 json 目前也是试验产品,不建议你在项目中使用,本文的目的只是让你体验下宏的优势,和未来代码编写的一个趋势方向。

感谢阅读本文

如果有什么建议,请在评论中让我知道。我很乐意改进。


flutter 学习路径


© 猫哥
ducafecat.com

end

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容