Flutter&JSON

JSON和序列化 - Flutter中文网

移动应用程序通常需要与 Web 服务器通信或存储结构化数据,而 JSON 是最常用的数据交换格式之一。

而在 Flutter 开发中,我们主要会用到两种 JSON 序列化方式:

  • 手动序列化

  • 代码生成自动序列化

1. 哪种 JSON 序列化方法适合我?

1.1 小项目:手动序列化

特点
  • 优点

    • 使用 Dart 内置的 dart:convert 库,无需额外依赖。

    • 对于简单 JSON 或模型较少的小项目非常方便。

  • 缺点

    • 直接使用 JSON.decode() 得到的是 Map<String, dynamic>,类型信息丢失。

    • 编译器无法捕捉字段名称的拼写错误,容易导致运行时异常。

    • 当模型增多或嵌套结构复杂时,手动编写转换代码会变得冗长且易出错。

示例

内联序列化JSON

直接使用 JSON.decode() 解码:

复制代码
 import 'dart:convert';
 ​
 void main() {
   String jsonString = '{"name": "John Smith", "email": "john@example.com"}';
   Map<String, dynamic> user = json.decode(jsonString);
 ​
   print('Howdy, ${user['name']}!');
   print('We sent the verification link to ${user['email']}!');
 }

在模型类中封装 JSON 序列化

通过创建模型类,将序列化逻辑封装到模型内部:

复制代码
 class User {
   final String name;
   final String email;
 ​
   User(this.name, this.email);
 ​
   // 从 JSON 构造 User 实例
   User.fromJson(Map<String, dynamic> json)
       : name = json['name'],
         email = json['email'];
 ​
   // 将 User 实例转换为 JSON
   Map<String, dynamic> toJson() => {
         'name': name,
         'email': email,
       };
 }

使用示例:

复制代码
 import 'dart:convert';
 ​
 void main() {
   String jsonString = '{"name": "John Smith", "email": "john@example.com"}';
   Map<String, dynamic> userMap = json.decode(jsonString);
   var user = User.fromJson(userMap);
 ​
   print('Howdy, ${user.name}!');
   print('We sent the verification link to ${user.email}!');
 ​
   // 序列化时,JSON.encode() 会自动调用 toJson 方法
   String encodedJson = json.encode(user);
   print(encodedJson);
 }

1.2 大中型项目:使用代码生成自动序列化

背景说明
  • 当项目中涉及多个 JSON 模型时,手动编写 fromJsontoJson 方法容易出错且维护成本高。

  • Flutter 中不存在像 Gson、Jackson、Moshi 这样的库,因为它们依赖运行时反射,而 Flutter 禁用了反射以支持 tree shaking,从而优化应用体积。

  • 类似 dartson 的库也依赖运行时反射,所以在 Flutter 中不可用。

代码生成的优势
  • 自动生成:通过工具自动生成序列化代码,避免手写重复代码。

  • 编译时检查:拼写错误等问题可以在编译期捕获,降低运行时异常风险。

  • 维护方便:当模型发生变化时,只需重新生成代码即可,无需手动修改序列化逻辑。

2. 使用 json_serializable 自动生成 JSON 序列化代码

2.1 设置项目依赖

在项目的 pubspec.yaml 中添加以下依赖项:

复制代码
 dependencies:
   # 其他依赖...
   json_annotation: ^2.0.0
 ​
 dev_dependencies:
   # 其他开发依赖...
   build_runner: ^1.0.0
   json_serializable: ^2.0.0

然后在项目根目录运行:

复制代码
 flutter packages get

2.2 创建模型类

使用 json_serializable 注解生成序列化代码。示例 User 模型如下:

复制代码
 import 'package:json_annotation/json_annotation.dart';
 ​
 // 生成的代码文件,运行 build_runner 后会自动生成
 part 'user.g.dart';
 ​
 @JsonSerializable()
 class User {
   String name;
   String email;
 ​
   User(this.name, this.email);
 ​
   // 通过生成的 _$UserFromJson 反序列化
   factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
 ​
   // 通过生成的 _$UserToJson 序列化
   Map<String, dynamic> toJson() => _$UserToJson(this);
 }
自定义 JSON 键名

若 API 返回的 JSON 键名与模型属性名不一致,可以使用 @JsonKey 进行映射,例如:

复制代码
 @JsonKey(name: 'registration_date_millis')
 final int registrationDateMillis;

2.3 运行代码生成器

有两种方式运行代码生成器,为模型自动生成序列化代码:

一次性生成

在项目根目录下运行:

复制代码
 flutter packages pub run build_runner build
持续生成(Watch 模式)

启动观察者模式,自动监视文件变化并生成代码:

复制代码
 flutter packages pub run build_runner watch

运行后,生成文件 user.g.dart 将包含所有序列化和反序列化的实现代码。

2.4 使用自动生成的模型

生成后的使用方法与手动方式类似:

复制代码
 import 'dart:convert';
 ​
 void main() {
   String jsonString = '{"name": "John Smith", "email": "john@example.com"}';
   Map<String, dynamic> userMap = json.decode(jsonString);
   var user = User.fromJson(userMap);
 ​
   print('Howdy, ${user.name}!');
   print('We sent the verification link to ${user.email}!');
 ​
   String encodedJson = json.encode(user);
   print(encodedJson);
 }

实践

原代码

复制代码
  if (response.statusCode == 200) {
       final responseData = jsonDecode(response.body);
       List<dynamic> items = responseData['data']['items'];
       List<String> photoUidsList = items
           .where((item) => item['type'] == 'photo')
           .map((item) => item['uid'] as String)
           .toList();//获取uid
 ​
       setState(() {
         photoUids = photoUidsList;
       });
     } else {
       throw Exception('Failed to load photos');
     }
   }

依据本篇文章来改进

加入

复制代码
 // 定义 Item 模型类,通过 fromJson 方法实现 JSON 数据解析
 class Item {
   final String type;
   final String uid;
 ​
   Item({required this.type, required this.uid});
 ​
   factory Item.fromJson(Map<String, dynamic> json) {
     return Item(
       type: json['type'] as String,
       uid: json['uid'] as String,
     );
   }
 }

原有部分替换为

复制代码
 class Item {
   final String type;
   final String uid;
 ​
   Item({required this.type, required this.uid});
   //构造函数,使用命名参数,并通过 required 强制必须传入两个属性值。
 ​
   factory Item.fromJson(Map<String, dynamic> json) {
     return Item(
       type: json['type'] as String,
       uid: json['uid'] as String,
     );
   }
 }//这个工厂构造函数 fromJson接收一个 Map<String, dynamic> 格式的 JSON 数据,并将其中的 type 和 uid 字段转换为 Item 对象。通过这种方式,可以轻松将 JSON 数据转换为 Dart 模型实例
 ​
 if (response.statusCode == 200) {
       final responseData = jsonDecode(response.body);
 //使用 jsonDecode(response.body) 将服务器返回的 JSON 格式字符串转换成 Dart 的数据结构(通常为 Map 或 List)
       List<dynamic> itemsJson = responseData['data']['items'];
       //取出嵌套在 data 内的 items 列表,并将其存储到变量 itemsJson 中
 ​
       // 将 JSON 数据映射为 Item 实例列表
       List<Item> items = itemsJson
           .map<Item>((json) => Item.fromJson(json))
           .toList();
 ​
       // 使用模型类的属性进行筛选和映射
       List<String> photoUid = items
           .where((item) => item.type == 'photo')
           .map((item) => item.uid)
           .toList();
 ​
       // 更新状态,刷新 UI
       setState(() {
         photoUids = photoUid;

总结

  • 手动序列化 :适合简单或模型较少的小项目,使用 dart:convert 和自定义模型类实现;缺点是容易出错且缺少编译时检查。

  • 代码生成自动序列化 :适合中大型项目,通过 json_serializable 自动生成序列化代码,提高类型安全和开发效率;初期需要进行一些配置和代码生成步骤。

  • Flutter 中不支持使用运行时反射的库(如 Gson/Jackson/Moshi),因此推荐使用基于代码生成的方案。

把 JSON 数据转换成静态类型明确的 Item 对象后,IDE 就能知道 item 的具体类型,从而提供属性和方法的自动补全,原理如下:

  1. 静态类型系统 Item 是一个明确的类,定义了 type 和 uid 两个属性。当你调用 item. 时,IDE 根据 Item 类的定义知道有哪些可用属性和方法,从而进行自动补全。

  2. 编译时类型检查 使用工厂构造函数 fromJson 将 Map<String, dynamic> 转换成 Item 对象后,编译器就能在编译期检查属性的正确性,减少因拼写错误或类型错误引起的运行时异常。

  3. 代码提示功能 静态类型信息使得 IDE(如 VS Code 或 Android Studio)能够提供精准的代码提示,帮助开发者快速查找和使用对象的属性或方法,而不必手动记住所有键名。

相关推荐
sinat_319868072 小时前
Spring Boot2.0之十 使用自定义注解、Json序列化器实现自动转换字典类型字段
spring boot·后端·json
bossface3 小时前
ES的简单讲解
服务器·c++·json·gtest·etcd·spdlog
村中少年4 小时前
使用DeepSeek/chatgpt等AI工具辅助网络协议流量数据包分析
网络协议·chatgpt·wireshark·json·aigc
l软件定制开发工作室5 小时前
Flutter系列教程之(5)——常用控件Widget的使用示例
flutter
leluckys5 小时前
flutter 专题 八十五 Flutter 应用调试
flutter
开源优测10 小时前
Pytest中5种不同的方法解析JSON数据
log4j·json·pytest
阿长长啊11 小时前
Flutter 介绍及安装使用
flutter
snail20121111 小时前
Flutter_boost混编开发系统MethodChannel之坑
flutter
leluckys11 小时前
flutter 专题 八十二 Flutter路由框架Fluro简介
开发语言·javascript·flutter