在这里插入图片描述
在这里插入图片描述

引言

在Flutter开发中,手动编写序列化和反序列化代码不仅效率低下,还容易引入错误。json_serializable是一个强大的代码生成包,可以自动生成类型安全的序列化代码,大大提高开发效率和代码质量。

本章节将深入探讨json_serializable的使用方法,结合天气查询应用场景,展示如何配置和使用这个工具。

1. json_serializable 概述

1.1 什么是 json_serializable

json_serializable是Dart官方推荐的代码生成包,它通过注解标记数据模型类,自动生成对应的序列化和反序列化代码。

1.2 核心优势

  • 类型安全:编译时检查,避免运行时类型错误
  • 自动生成:减少手动编写样板代码
  • 易于维护:数据模型变更后,重新生成即可
  • 社区成熟:广泛使用,文档完善

1.3 工作原理

  1. 在数据模型类上添加@JsonSerializable()注解
  2. 运行代码生成命令
  3. 自动生成.g.dart文件,包含序列化和反序列化代码
  4. 在模型类中调用生成的方法

2. 环境配置

2.1 添加依赖

pubspec.yaml中添加以下依赖:

dependencies:
  json_annotation: ^4.8.1

dev_dependencies:
  build_runner: ^2.4.8
  json_serializable: ^6.7.1

代码解析

  • json_annotation:提供注解定义,运行时依赖
  • build_runner:代码生成工具,开发依赖
  • json_serializable:代码生成器,开发依赖

2.2 安装依赖

flutter pub get

2.3 配置 build.yaml(可选)

创建build.yaml文件,配置代码生成选项:

targets:
  $default:
    builders:
      json_serializable:
        options:
          explicit_to_json: true
          any_map: false
          checked: false
          create_factory: true
          create_to_json: true

配置说明

选项说明默认值
explicit_to_json是否显式调用嵌套对象的toJsonfalse
any_map使用Map<dynamic, dynamic>而非Map<String, dynamic>false
checked生成检查代码,确保类型安全false
create_factory是否生成fromJson工厂方法true
create_to_json是否生成toJson方法true

3. 创建数据模型

3.1 基础模型定义

import "package:json_annotation/json_annotation.dart";

part "weather.g.dart";

()
class Weather {
  final String city;
  (name: "temp")
  final double temperature;
  final String condition;
  (defaultValue: "")
  final String description;

  Weather({
    required this.city,
    required this.temperature,
    required this.condition,
    this.description = "",
  });

  factory Weather.fromJson(Map<String, dynamic> json) =>
      _$WeatherFromJson(json);

  Map<String, dynamic> toJson() => _$WeatherToJson(this);
}

代码解析

  1. part指令:引用生成的代码文件
  2. @JsonSerializable()注解:标记需要生成序列化代码的类
  3. @JsonKey注解:自定义JSON字段映射
  4. fromJson工厂方法:调用生成的反序列化方法
  5. toJson方法:调用生成的序列化方法

3.2 @JsonKey注解详解

@JsonKey注解提供了丰富的配置选项:

(name: "user_name")
final String userName;

(defaultValue: 0)
final int count;

(ignore: true)
final String? password;

(fromJson: _dateTimeFromJson, toJson: _dateTimeToJson)
final DateTime timestamp;

(required: true)
final String email;

(disallowNullValue: true)
final String name;

@JsonKey参数说明

参数类型说明
nameStringJSON中的字段名
defaultValuedynamic默认值
ignorebool是否忽略此字段
fromJsonFunction自定义反序列化函数
toJsonFunction自定义序列化函数
requiredbool是否必填
disallowNullValuebool是否禁止null值

4. 运行代码生成

4.1 单次生成

flutter pub run build_runner build

4.2 持续监听

flutter pub run build_runner watch

代码解析

  • watch模式会监听文件变化,自动重新生成代码
  • 适合开发过程中使用,避免频繁手动运行

4.3 清理并重新生成

flutter pub run build_runner clean
flutter pub run build_runner build --delete-conflicting-outputs

代码解析

  • clean命令删除所有生成的文件
  • --delete-conflicting-outputs参数强制覆盖冲突文件

5. 生成的代码分析

5.1 生成的文件结构

运行代码生成后,会生成weather.g.dart文件:

// weather.g.dart
Weather _$WeatherFromJson(Map<String, dynamic> json) => Weather(
  city: json["city"] as String,
  temperature: (json["temp"] as num).toDouble(),
  condition: json["condition"] as String,
  description: json["description"] as String? ?? "",
);

Map<String, dynamic> _$WeatherToJson(Weather instance) => <String, dynamic>{
  "city": instance.city,
  "temp": instance.temperature,
  "condition": instance.condition,
  "description": instance.description,
};

代码解析

  1. _$WeatherFromJson:反序列化方法

    • 将Map转换为Weather对象
    • 处理类型转换,如(json["temp"] as num).toDouble()
    • 处理默认值,如json["description"] as String? ?? ""
  2. _$WeatherToJson:序列化方法

    • 将Weather对象转换为Map
    • 处理字段映射,如"temp": instance.temperature

5.2 类型转换策略

生成的代码会自动处理类型转换:

// 数字类型
temperature: (json["temp"] as num).toDouble(),

// 字符串类型
city: json["city"] as String,

// 可选字段
description: json["description"] as String? ?? "",

// 列表类型
forecast: (json["forecast"] as List<dynamic>)
    .map((e) => ForecastDay.fromJson(e as Map<String, dynamic>))
    .toList(),

6. 复杂模型示例

6.1 嵌套对象

import "package:json_annotation/json_annotation.dart";

part "weather_response.g.dart";

()
class WeatherResponse {
  final WeatherData weather;

  WeatherResponse({required this.weather});

  factory WeatherResponse.fromJson(Map<String, dynamic> json) =>
      _$WeatherResponseFromJson(json);

  Map<String, dynamic> toJson() => _$WeatherResponseToJson(this);
}

()
class WeatherData {
  final String city;
  final CurrentWeather current;
  final List<ForecastDay> forecast;
  final DateTime lastUpdated;

  WeatherData({
    required this.city,
    required this.current,
    required this.forecast,
    required this.lastUpdated,
  });

  factory WeatherData.fromJson(Map<String, dynamic> json) =>
      _$WeatherDataFromJson(json);

  Map<String, dynamic> toJson() => _$WeatherDataToJson(this);
}

()
class CurrentWeather {
  final double temp;
  final int humidity;
  final String condition;

  CurrentWeather({
    required this.temp,
    required this.humidity,
    required this.condition,
  });

  factory CurrentWeather.fromJson(Map<String, dynamic> json) =>
      _$CurrentWeatherFromJson(json);

  Map<String, dynamic> toJson() => _$CurrentWeatherToJson(this);
}

()
class ForecastDay {
  final String date;
  final int high;
  final int low;

  ForecastDay({
    required this.date,
    required this.high,
    required this.low,
  });

  factory ForecastDay.fromJson(Map<String, dynamic> json) =>
      _$ForecastDayFromJson(json);

  Map<String, dynamic> toJson() => _$ForecastDayToJson(this);
}

6.2 自定义类型转换

对于DateTime等特殊类型,可以自定义转换函数:

import "package:json_annotation/json_annotation.dart";

part "weather.g.dart";

DateTime _dateTimeFromJson(String str) => DateTime.parse(str);
String _dateTimeToJson(DateTime date) => date.toIso8601String();

()
class Weather {
  final String city;
  final double temperature;
  
  (fromJson: _dateTimeFromJson, toJson: _dateTimeToJson)
  final DateTime lastUpdated;

  Weather({
    required this.city,
    required this.temperature,
    required this.lastUpdated,
  });

  factory Weather.fromJson(Map<String, dynamic> json) =>
      _$WeatherFromJson(json);

  Map<String, dynamic> toJson() => _$WeatherToJson(this);
}

代码解析

  • _dateTimeFromJson:将字符串转换为DateTime
  • _dateTimeToJson:将DateTime转换为字符串
  • 使用@JsonKeyfromJsontoJson参数指定自定义转换函数

7. 使用示例

7.1 基本使用

void useGeneratedCode() {
  String jsonStr = '{"city":"北京","temp":28.5,"condition":"晴"}';

  // 反序列化
  Weather weather = Weather.fromJson(json.decode(jsonStr));
  print("${weather.city}: ${weather.temperature}度");

  // 序列化
  String encoded = json.encode(weather);
  print(encoded);
}

7.2 解析API响应

Future<Weather> fetchWeather(String city) async {
  final response = await http.get(Uri.parse(
      "https://api.example.com/weather?city=$city"));

  if (response.statusCode == 200) {
    return Weather.fromJson(json.decode(response.body));
  } else {
    throw Exception("获取天气失败");
  }
}

7.3 序列化并存储

void saveWeather(Weather weather) async {
  final prefs = await SharedPreferences.getInstance();
  String jsonStr = json.encode(weather);
  await prefs.setString("weather_data", jsonStr);
}

Future<Weather?> loadWeather() async {
  final prefs = await SharedPreferences.getInstance();
  String? jsonStr = prefs.getString("weather_data");
  
  if (jsonStr != null) {
    return Weather.fromJson(json.decode(jsonStr));
  }
  return null;
}

8. 高级配置

8.1 忽略字段

()
class User {
  final String name;
  
  (ignore: true)
  final String? password;

  User({required this.name, this.password});

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

代码解析

  • password字段不会参与序列化和反序列化
  • 适合敏感信息或临时数据

8.2 默认值

()
class Weather {
  final String city;
  
  (defaultValue: 0.0)
  final double temperature;
  
  (defaultValue: "未知")
  final String condition;

  Weather({
    required this.city,
    required this.temperature,
    required this.condition,
  });

  factory Weather.fromJson(Map<String, dynamic> json) => _$WeatherFromJson(json);
  Map<String, dynamic> toJson() => _$WeatherToJson(this);
}

8.3 字段重命名

()
class User {
  (name: "user_name")
  final String userName;
  
  (name: "age")
  final int userAge;

  User({required this.userName, required this.userAge});

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

9. 与freezed结合使用

9.1 添加依赖

dependencies:
  json_annotation: ^4.8.1
  freezed_annotation: ^2.4.4

dev_dependencies:
  build_runner: ^2.4.8
  json_serializable: ^6.7.1
  freezed: ^2.5.7

9.2 创建freezed模型

import "package:json_annotation/json_annotation.dart";
import "package:freezed_annotation/freezed_annotation.dart";

part "weather.freezed.dart";
part "weather.g.dart";


()
class Weather with _$Weather {
  const factory Weather({
    required String city,
    (name: "temp") required double temperature,
    required String condition,
    ("") String description,
  }) = _Weather;

  factory Weather.fromJson(Map<String, dynamic> json) =>
      _$WeatherFromJson(json);
}

代码解析

  • @freezed注解:生成不可变模型代码
  • @JsonSerializable()注解:生成序列化代码
  • @Default(""):提供默认值
  • 自动生成copyWith方法,方便对象修改

10. 性能优化

10.1 预编译模型

对于频繁使用的模型,可以在应用启动时预编译:

void main() {
  // 预编译JSON解码器
  json.decode('{}');
  
  runApp(const MyApp());
}

10.2 延迟解析

对于大型数据集,可以考虑延迟解析:

()
class WeatherData {
  final String city;
  
  // 使用JsonConverter实现延迟解析
  (fromJson: _parseForecast)
  final List<ForecastDay> forecast;

  WeatherData({required this.city, required this.forecast});

  factory WeatherData.fromJson(Map<String, dynamic> json) =>
      _$WeatherDataFromJson(json);
}

List<ForecastDay> _parseForecast(List<dynamic> list) {
  return list.map((e) => ForecastDay.fromJson(e)).toList();
}

11. 常见问题

11.1 生成代码失败

问题:运行build_runner后没有生成.g.dart文件

解决方案

  1. 检查是否添加了part "xxx.g.dart";指令
  2. 检查是否添加了@JsonSerializable()注解
  3. 检查依赖版本是否兼容
  4. 运行flutter pub run build_runner clean后重新生成

11.2 类型转换错误

问题:生成的代码出现类型转换错误

解决方案

  1. 检查JSON字段类型是否与模型定义一致
  2. 使用@JsonKeyfromJson参数自定义转换
  3. 对于数字类型,确保使用(json["field"] as num).toDouble()

11.3 嵌套对象序列化

问题:嵌套对象序列化时出现错误

解决方案

  1. 确保嵌套对象也添加了@JsonSerializable()注解
  2. build.yaml中设置explicit_to_json: true

12. 最佳实践

12.1 项目结构

lib/
├── models/
│   ├── weather.dart
│   ├── weather.g.dart
│   ├── forecast.dart
│   └── forecast.g.dart
└── ...

12.2 代码组织

  1. 集中管理:将所有数据模型放在同一目录下
  2. 命名规范:模型文件使用小写蛇形命名(如weather_model.dart
  3. 代码生成:将生成的.g.dart文件纳入版本控制

12.3 开发流程

  1. 定义模型:创建数据模型类并添加注解
  2. 生成代码:运行build_runner生成序列化代码
  3. 使用模型:在业务代码中使用生成的方法
  4. 更新模型:修改模型后重新生成代码

13. 总结

json_serializable是Flutter开发中处理JSON序列化的首选工具。它通过代码生成机制,自动生成类型安全的序列化代码,大大提高了开发效率和代码质量。

本章详细介绍了json_serializable的配置、使用和高级特性,结合天气查询应用场景展示了实际应用。掌握这个工具对于构建高质量的Flutter应用至关重要。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐