flutter-implement-json-serialization

flutter-implement-json-serialization

热门

使用 `dart:convert` 创建包含 `fromJson` 与 `toJson` 方法的模型类。适用于简单数据结构下手动将 JSON 键映射为类属性的场景。

2783Star
163Fork
更新于 2026/8/5
SKILL.md
只读
名称
flutter-implement-json-serialization
描述

使用 `dart:convert` 创建包含 `fromJson` 与 `toJson` 方法的模型类。适用于简单数据结构下手动将 JSON 键映射为类属性的场景。

在 Flutter 中手动序列化 JSON

目录

核心准则

  • 引入 dart:convert:使用 Flutter 内置的 dart:convert 库来进行手动 JSON 编码(jsonEncode)与解码(jsonDecode)。
  • 强类型安全:务必将 jsonDecode() 返回的 dynamic 类型强制转换为预期类型,通常对象转为 Map<String, dynamic>,数组转为 List<dynamic>
  • 封装序列化逻辑:定义包含与 JSON 结构相对应属性的纯模型类(Plain Model Class)。在模型内部实现 fromJson 工厂构造函数与 toJson 方法。
  • 后台异步解析:若解析大型 JSON 文档(耗时大于 16ms),请使用 Flutter 的 compute() 函数将解析逻辑切到独立 Isolate 中运行,避免造成 UI 卡顿。
  • 失败时抛出异常:处理 HTTP 响应时,若状态码非成功状态(例如不属于 200 OK 或 201 Created),应直接抛出异常,切勿返回 null

工作流:实现可序列化模型

使用以下清单为数据模型实现手动 JSON 序列化。

任务进度:

  • [ ] 定义带有 final 属性的纯模型类。
  • [ ] 实现 factory Model.fromJson(Map<String, dynamic> json) 构造函数。
  • [ ] 实现 Map<String, dynamic> toJson() 方法。
  • [ ] 为这两种序列化方法编写单元测试。
  • [ ] 运行校验工具 -> 检查类型不匹配错误 -> 修复类型转换逻辑。
  1. 定义模型:创建一个类,其属性与 JSON 字段的键(Key)保持一致。
  2. 实现 fromJson:从 Map 中提取值并转换为相对应的 Dart 类型。可以使用模式匹配或显式类型转换。
  3. 实现 toJson:返回一个 Map<String, dynamic>,将类属性映射回对应的 JSON 字符串键。
  4. 测试验证:运行单元测试,确保类型安全、代码补全以及编译期异常处理均正常工作。

工作流:获取与解析 JSON

在通过网络请求获取并解析 JSON 时,请遵循以下分支工作流。

任务进度:

  • [ ] 发起 HTTP 请求。
  • [ ] 校验响应状态码。
  • [ ] 确定解析策略(同步解析 vs. Isolate 后台解析)。
  • [ ] 解码并将 JSON 映射至模型对象。
  1. 发起请求:使用 http Package 发起网络调用。
  2. 校验响应
    • response.statusCode == 200(或 POST 请求为 201),则进入解析流程。
    • 若状态码提示请求失败,直接抛出 Exception
  3. 确定解析策略
    • 解析小数据包(例如单个对象):直接在主线程同步解析。
    • 解析大数据包(例如包含数千个对象的数组):使用 compute(parseFunction, response.body) 在后台 Isolate 中解析。
  4. 解码与映射:将解码后的 JSON 传入模型的 fromJson 构造函数中。

示例

高完备度模型实现

import 'dart:convert';

class User {
  final int id;
  final String name;
  final String email;

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

  // 用于反序列化的工厂构造函数
  factory User.fromJson(Map<String, dynamic> json) {
    return switch (json) {
      {
        'id': int id,
        'name': String name,
        'email': String email,
      } => 
        User(
          id: id,
          name: name,
          email: email,
        ),
      _ => throw const FormatException('Failed to load User.'),
    };
  }

  // 用于序列化的方法
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'name': name,
      'email': email,
    };
  }
}

同步解析(小数据包)

import 'dart:convert';
import 'package:http/http.dart' as http;

Future<User> fetchUser(http.Client client, int userId) async {
  final response = await client.get(
    Uri.parse('https://api.example.com/users/$userId'),
    headers: {'Accept': 'application/json'},
  );

  if (response.statusCode == 200) {
    // jsonDecode 返回 dynamic,需显式转换为 Map<String, dynamic>
    final Map<String, dynamic> jsonMap = jsonDecode(response.body) as Map<String, dynamic>;
    return User.fromJson(jsonMap);
  } else {
    throw Exception('Failed to load user');
  }
}

后台解析(大数据包)

import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;

// compute() 需要顶层函数 (Top-level function)
List<User> parseUsers(String responseBody) {
  final parsed = (jsonDecode(responseBody) as List<dynamic>).cast<Map<String, dynamic>>();
  return parsed.map<User>((json) => User.fromJson(json)).toList();
}

Future<List<User>> fetchUsers(http.Client client) async {
  final response = await client.get(
    Uri.parse('https://api.example.com/users'),
    headers: {'Accept': 'application/json'},
  );

  if (response.statusCode == 200) {
    // 将耗时的解析任务下发到后台 Isolate 运行
    return compute(parseUsers, response.body);
  } else {
    throw Exception('Failed to load users');
  }
}