'# Flutter Json自动反序列化——json_serializable v1
一、背景与问题
在Flutter开发中,处理网络请求返回的JSON数据时,开发者通常需要手动编写大量的反序列化代码。以一个简单的用户模型为例:
class User {
final String name;
final int age;
User({required this.name, required this.age});
factory User.fromJson(Map<String, dynamic> json) {
return User(
name: json['name'] as String,
age: json['age'] as int,
);
}
Map<String, dynamic> toJson() {
return {
'name': name,
'age': age,
};
}
}
这种手动编码方式存在以下问题:
- 代码冗余,特别是当模型包含大量字段时
- 容易因字段类型转换错误导致运行时崩溃
- 需要维护两个方向的转换方法(fromJson/toJson)
- 无法自动处理嵌套结构和复杂类型
- 在团队协作中容易出现字段命名不一致的问题
为了解决这些问题,Dart社区开发了json_serializable库。该库通过代码生成技术,将模型类的注解转换为可运行的反序列化代码,大幅减少手动编码量。
二、基本原理
json_serializable的核心原理是通过注解处理器生成代码。其工作流程如下:
- 开发者在模型类中添加注解
- 使用
build_runner工具运行注解处理器 - 生成包含反序列化逻辑的代码文件
- 在运行时通过
JsonSerializable类进行序列化/反序列化
关键组件包括:
@JsonSerializable注解:标记需要生成代码的类@JsonKey注解:控制字段的序列化/反序列化行为JsonSerializable类:生成的反序列化代码核心JsonDecoder/JsonEncoder:处理具体的数据转换
三、环境准备
在使用前需要配置以下依赖:
dependencies:
flutter:
sdk: flutter
json_annotation: ^4.8.0
dev_dependencies:
build_runner: ^2.1.8
注意:json_annotation是核心库,build_runner用于生成代码。需要确保版本兼容性,通常建议使用最新稳定版。
四、核心实现
1. 基础模型类
import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart';
@JsonSerializable()
class User {
final String name;
final int age;
User({required this.name, required this.age});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
关键点:
@JsonSerializable()注解标记需要生成代码的类part 'user.g.dart'声明生成的代码文件fromJson/toJson方法调用生成的函数
2. 生成代码
运行以下命令生成代码:
flutter pub run build_runner build --delete-build-dir
生成的代码包含:
// user.g.dart
@JsonSerializable()
class User {
final String name;
final int age;
User({required this.name, required this.age});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
User _$UserFromJson(Map<String, dynamic> json) {
return User(
name: json['name'] as String,
age: json['age'] as int,
);
}
Map<String, dynamic> _$UserToJson(User instance) {
return <String, dynamic>{
'name': instance.name,
'age': instance.age,
};
}
3. 使用生成代码
void main() async {
final json = '''
{
"name": "Alice",
"age": 30
}
''';
final user = User.fromJson(jsonDecode(json));
print(user.name); // 输出 Alice
final json2 = user.toJson();
print(jsonEncode(json2)); // 输出 {"name": "Alice", "age": 30}
}
五、完整案例
1. 天气预报应用模型
// weather.g.dart
@JsonSerializable()
class Weather {
final String city;
final double temperature;
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);
}
// forecast.g.dart
@JsonSerializable()
class Forecast {
final List<Weather> dailyForecasts;
Forecast({required this.dailyForecasts});
factory Forecast.fromJson(Map<String, dynamic> json) => _$ForecastFromJson(json);
Map<String, dynamic> toJson() => _$ForecastToJson(this);
}
2. 网络请求示例
import 'dart:convert';
import 'package:http/http.dart' as http;
Future<Forecast> fetchWeatherForecast() async {
final response = await http.get(Uri.parse('https://api.example.com/forecast'));
if (response.statusCode == 200) {
return Forecast.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load forecast');
}
}
六、源码解析
1. 生成代码结构
生成的代码包含以下部分:
- 构造函数
fromJson工厂方法toJson方法- 生成的函数(如
_$UserFromJson)
2. 代码生成机制
json_serializable使用Dart的代码生成器,通过解析注解生成代码。核心处理逻辑如下:
// 伪代码示例
void generateCode(Class clazz) {
for (Field field in clazz.fields) {
generateFieldCode(field);
}
generateFactoryMethod(clazz);
generateToJsonMethod(clazz);
}
3. 关键函数解析
Map<String, dynamic> _$UserToJson(User instance) {
return <String, dynamic>{
'name': instance.name,
'age': instance.age,
};
}
- 该函数将对象属性映射为Map
- 通过反射获取字段信息
- 支持复杂类型转换(如List、Map、自定义类型)
七、进阶使用
1. 处理嵌套结构
@JsonSerializable()
class Address {
final String street;
final String city;
Address({required this.street, required this.city});
factory Address.fromJson(Map<String, dynamic> json) => _$AddressFromJson(json);
Map<String, dynamic> toJson() => _$AddressToJson(this);
}
@JsonSerializable()
class User {
final String name;
final Address address;
User({required this.name, required this.address});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
2. 忽略字段
@JsonSerializable()
class User {
final String name;
final int age;
final String? token; // 会忽略
User({required this.name, required this.age, this.token});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
3. 自定义字段映射
@JsonSerializable()
class User {
final String name;
@JsonKey(name: 'user_age')
final int age;
User({required this.name, required this.age});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
八、性能与工程实践
1. 性能优化
- 避免过度使用
@JsonSerializable注解 - 对于简单模型,使用
dart:convert的decode方法 - 使用
@JsonKey优化字段映射 - 对大型模型使用
@JsonSerializable+@JsonInclude组合
2. 异常处理
try {
final user = User.fromJson(jsonDecode(json));
} catch (e) {
print('反序列化失败: $e');
}
3. 安全考虑
- 对接收到的JSON数据进行合法性校验
- 避免直接暴露敏感字段
- 对自定义类型进行安全检查
九、常见问题与踩坑
1. 常见错误
错误示例:
// 忘记运行build_runner
final user = User.fromJson(jsonDecode(json));
解决方案:
运行flutter pub run build_runner build生成代码
错误示例:
// 字段类型不匹配
final user = User.fromJson({'name': 123});
解决方案:
确保JSON字段类型与模型类匹配
2. 踩坑指南
- 在
pubspec.yaml中不要遗漏dev_dependencies中的build_runner - 多个模型文件需要分别运行
build_runner - 修改模型类后需要重新生成代码
- 对于复杂类型需要手动实现
fromJson/toJson
十、最佳实践
- 复杂模型:使用json_serializable处理嵌套结构
- 简单模型:直接使用
dart:convert的decode方法 - 团队协作:统一字段命名规范
- 安全处理:对敏感字段进行加密处理
- 性能优化:对大型模型使用
@JsonInclude减少字段数量
十一、总结
json_serializable通过代码生成技术,为Flutter开发者提供了一种高效、安全的JSON反序列化方案。其核心优势在于:
- 自动生成反序列化代码
- 支持复杂类型和嵌套结构
- 提供字段映射控制
- 避免手动编写大量重复代码
在实际开发中,建议:
- 对复杂模型使用json_serializable
- 对简单模型使用内置方法
- 保持代码结构清晰
- 注意生成代码的更新维护
需要注意的是,json_serializable生成的代码在运行时会消耗额外资源,对于对性能要求极高的场景,可以考虑使用更底层的实现方式。但总体而言,它在提升开发效率和代码可维护性方面具有显著优势。