flutter-implement-json-serialization

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Serializing JSON Manually in Flutter

在Flutter中手动序列化JSON

Contents

目录

Core Guidelines

核心准则

  • Import
    dart:convert
    : Utilize Flutter's built-in
    dart:convert
    library for manual JSON encoding (
    jsonEncode
    ) and decoding (
    jsonDecode
    ).
  • Enforce Type Safety: Always cast the
    dynamic
    result of
    jsonDecode()
    to the expected type, typically
    Map<String, dynamic>
    for objects or
    List<dynamic>
    for arrays.
  • Encapsulate Serialization Logic: Define plain model classes containing properties corresponding to the JSON structure. Implement a
    fromJson
    factory constructor and a
    toJson
    method within the model.
  • Handle Background Parsing: If parsing large JSON documents (execution time > 16ms), offload the parsing logic to a separate isolate using Flutter's
    compute()
    function to prevent UI jank.
  • Throw Exceptions on Failure: When handling HTTP responses, throw an exception if the status code is not successful (e.g., not 200 OK or 201 Created). Do not return
    null
    .
  • 导入
    dart:convert
    :利用Flutter内置的
    dart:convert
    库进行手动JSON编码(
    jsonEncode
    )和解码(
    jsonDecode
    )。
  • 强制类型安全:始终将
    jsonDecode()
    返回的
    dynamic
    类型结果转换为预期类型,通常对象对应
    Map<String, dynamic>
    ,数组对应
    List<dynamic>
  • 封装序列化逻辑:定义与JSON结构对应的普通模型类,在模型内部实现
    fromJson
    工厂构造函数和
    toJson
    方法。
  • 处理后台解析:如果解析大型JSON文档(执行时间>16ms),使用Flutter的
    compute()
    函数将解析逻辑卸载到单独的isolate中,以避免UI卡顿。
  • 失败时抛出异常:处理HTTP响应时,如果状态码未表示成功(例如不是200 OK或201 Created),抛出异常,不要返回
    null

Workflow: Implementing a Serializable Model

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

Use this checklist to implement manual JSON serialization for a data model.
Task Progress:
  • Define the plain model class with
    final
    properties.
  • Implement the
    factory Model.fromJson(Map<String, dynamic> json)
    constructor.
  • Implement the
    Map<String, dynamic> toJson()
    method.
  • Write unit tests for both serialization methods.
  • Run validator -> review type mismatch errors -> fix casting logic.
  1. Define the Model: Create a class with properties matching the JSON keys.
  2. Implement
    fromJson
    : Extract values from the
    Map
    and cast them to the appropriate Dart types. Use pattern matching or explicit casting.
  3. Implement
    toJson
    : Return a
    Map<String, dynamic>
    mapping the class properties back to their JSON string keys.
  4. Validate: Execute unit tests to ensure type safety, autocompletion, and compile-time exception handling function correctly.
使用此检查清单为数据模型实现手动JSON序列化。
任务进度:
  • 定义带有
    final
    属性的普通模型类。
  • 实现
    factory Model.fromJson(Map<String, dynamic> json)
    构造函数。
  • 实现
    Map<String, dynamic> toJson()
    方法。
  • 为两种序列化方法编写单元测试。
  • 运行验证器 -> 检查类型不匹配错误 -> 修复类型转换逻辑。
  1. 定义模型:创建属性与JSON键匹配的类。
  2. 实现
    fromJson
    :从
    Map
    中提取值并转换为合适的Dart类型,使用模式匹配或显式类型转换。
  3. 实现
    toJson
    :返回一个
    Map<String, dynamic>
    ,将类属性映射回对应的JSON字符串键。
  4. 验证:执行单元测试,确保类型安全、自动补全和编译时异常处理功能正常。

Workflow: Fetching and Parsing JSON

工作流程:获取并解析JSON

Use this conditional workflow when retrieving and parsing JSON from a network request.
Task Progress:
  • Execute the HTTP request.
  • Validate the response status code.
  • Determine parsing strategy (Synchronous vs. Isolate).
  • Decode and map the JSON to the model.
  1. Execute Request: Use the
    http
    package to perform the network call.
  2. Validate Response:
    • If
      response.statusCode == 200
      (or 201 for POST), proceed to parsing.
    • If the status code indicates failure, throw an
      Exception
      .
  3. Determine Parsing Strategy:
    • If parsing a small payload (e.g., a single object), parse synchronously on the main thread.
    • If parsing a large payload (e.g., an array of thousands of objects), use
      compute(parseFunction, response.body)
      to parse in a background isolate.
  4. Decode and Map: Pass the decoded JSON to your model's
    fromJson
    constructor.
当从网络请求中获取并解析JSON时,使用此条件工作流程。
任务进度:
  • 执行HTTP请求。
  • 验证响应状态码。
  • 确定解析策略(同步vs. Isolate)。
  • 解码并将JSON映射到模型。
  1. 执行请求:使用
    http
    包执行网络调用。
  2. 验证响应
    • 如果
      response.statusCode == 200
      (POST请求为201),继续解析。
    • 如果状态码表示失败,抛出
      Exception
  3. 确定解析策略
    • 如果解析小负载(例如单个对象),在主线程上同步解析。
    • 如果解析大负载(例如数千个对象的数组),使用
      compute(parseFunction, response.body)
      在后台isolate中解析。
  4. 解码并映射:将解码后的JSON传递给模型的
    fromJson
    构造函数。

Examples

示例

High-Fidelity Model Implementation

高保真模型实现

dart
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 constructor for deserialization
  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.'),
    };
  }

  // Method for serialization
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'name': name,
      'email': email,
    };
  }
}
dart
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 constructor for deserialization
  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.'),
    };
  }

  // Method for serialization
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'name': name,
      'email': email,
    };
  }
}

Synchronous Parsing (Small Payload)

同步解析(小负载)

dart
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) {
    // Decode returns dynamic, cast to 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');
  }
}
dart
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) {
    // Decode returns dynamic, cast to 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');
  }
}

Background Parsing (Large Payload)

后台解析(大负载)

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

// Top-level function required for compute()
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) {
    // Offload expensive parsing to a background isolate
    return compute(parseUsers, response.body);
  } else {
    throw Exception('Failed to load users');
  }
}
dart
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;

// Top-level function required for compute()
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) {
    // Offload expensive parsing to a background isolate
    return compute(parseUsers, response.body);
  } else {
    throw Exception('Failed to load users');
  }
}