json_annotation 是一个 Dart 库,通常用于生成 JSON 序列化和反序列化的代码。它提供了一系列注解,帮助开发者更方便地将 Dart 对象与 JSON 数据进行相互转换。
安装依赖
在 pubspec.yaml 文件中添加 json_annotation 和 json_serializable 依赖:
dependencies:
json_annotation: ^4.8.1
dev_dependencies:
build_runner: ^2.4.6
json_serializable: ^6.6.1
基础使用
假设我们要创建一个简单的 Person 类,包含 name 和 age 两个属性。
import 'package:json_annotation/json_annotation.dart';
// 生成的序列化代码文件
part 'person.g.dart';
// 使用 @JsonSerializable 注解标记该类
@JsonSerializable()
class Person {
// 类的属性
final String name;
final int age;
// 构造函数
Person(this.name, this.age);
// 工厂方法,用于从 JSON 数据反序列化对象
factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json);
// 方法,用于将对象序列化为 JSON 数据
Map<String, dynamic> toJson() => _$PersonToJson(this);
}
在上述代码中:
part 'person.g.dart';表示引入生成的序列化代码文件。@JsonSerializable()是json_annotation提供的注解,用于标记需要进行 JSON 序列化和反序列化的类。factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json);是一个工厂构造函数,用于从 JSON 数据创建Person对象。Map<String, dynamic> toJson() => _$PersonToJson(this);方法用于将Person对象转换为 JSON 数据。
常用注解
@JsonKey
用于指定 JSON 键名、默认值、忽略序列化等。
const JsonKey({
@Deprecated('Has no effect') bool? nullable,
this.defaultValue,
this.disallowNullValue,
this.fromJson,
@Deprecated(
'Use `includeFromJson` and `includeToJson` with a value of `false` '
'instead.',
)
this.ignore,
this.includeFromJson,
this.includeIfNull,
this.includeToJson,
this.name,
this.readValue,
this.required,
this.toJson,
this.unknownEnumValue,
});
-
name指定该属性在 JSON 数据中对应的键名。当 Dart 类属性名与 JSON 键名不一致时,可使用此参数进行映射。// 指定 JSON 键名 @JsonKey(name: 'product_name') final String name; -
defaultValue指定该属性在 JSON 数据中不存在时的默认值。当反序列化时,如果 JSON 中没有对应的键,会使用此默认值。// 指定默认值 @JsonKey(defaultValue: true) final bool isEnabled; -
ignore如果设置为true,则该属性在序列化和反序列化过程中会被忽略,不会出现在生成的 JSON 数据中,也不会从 JSON 数据中读取// 忽略序列化和反序列化 @JsonKey(ignore: true) final String password; -
toJson和fromJson分别指定自定义的序列化和反序列化函数。当默认的序列化和反序列化规则不满足需求时,可使用这两个参数自定义转换逻辑。// 自定义日期反序列化函数 DateTime _dateFromJson(String json) => DateTime.parse(json); // 自定义日期序列化函数 String _dateToJson(DateTime date) => date.toIso8601String(); @JsonSerializable() class DateExample { @JsonKey(fromJson: _dateFromJson, toJson: _dateToJson) final DateTime date; DateExample(this.date); factory DateExample.fromJson(Map<String, dynamic> json) => _$DateExampleFromJson(json); Map<String, dynamic> toJson() => _$DateExampleToJson(this); } -
includeIfNull如果设置为false,当该属性的值为null时,在序列化过程中不会将该属性包含在生成的 JSON 数据中。
@JsonKey(includeIfNull: false)
final String description;
@JsonSerializable
这是 json_annotation 中最核心的注解,用于标记需要进行 JSON 序列化和反序列化的类。它会告诉 json_serializable 代码生成器为该类生成相应的 fromJson 和 toJson 方法
const JsonSerializable({
this.anyMap,
this.checked,
this.constructor,
this.createFieldMap,
this.createJsonKeys,
this.createFactory,
this.createToJson,
this.disallowUnrecognizedKeys,
this.explicitToJson,
this.fieldRename,
this.ignoreUnannotated,
this.includeIfNull,
this.converters,
this.genericArgumentFactories,
this.createPerFieldToJson,
});
anyMap默认值:false 如果设置为 true,生成的代码将支持 Map<dynamic, dynamic> 类型的 JSON 数据,而不仅仅是 Map<String, dynamic>。checked默认值:false 如果设置为 true,生成的代码在反序列化时会进行类型检查,若类型不匹配会抛出异常。createFactory默认值:true 如果设置为 false,将不会生成 fromJson 工厂构造函数。createToJson默认值:true 如果设置为 false,将不会生成 toJson 方法。disallowUnrecognizedKeys默认值:false 如果设置为 true,在反序列化时遇到 JSON 数据中存在类中未定义的键,会抛出异常。explicitToJson默认值:false 如果设置为 true,生成的 toJson 方法只会包含类中使用 @JsonKey 注解明确指定的属性。fieldRename默认值:FieldRename.none 指定属性名在 JSON 中的重命名策略,常见的策略有 FieldRename.snake(蛇形命名)、FieldRename.camel(驼峰命名)等。
防止类型解析失败:定义转换器
对于上面的person对象,我们解析json生成响应model
// 定义常量 JSON 数据
const jsonData = {'name': 'Alice', 'age': "25"};
Person p = Person.fromJson(jsonData);
类型解析失败,因为我们json定义的age是字符串
我们怎么解决这个问题呢?
我们可以通过json_annotation定义一些转换器来解决
- 1、定义一个SafeNumConverter转换器
import 'package:json_annotation/json_annotation.dart';
class SafeNumConverter extends JsonConverter<num, dynamic> {
final num defaultValue;
const SafeNumConverter({this.defaultValue = -10086});
@override
num fromJson(dynamic json) {
if (json is num) {
return json;
}
if (json is String) {
try {
return num.parse(json);
} catch (e) {
return defaultValue;
}
}
return defaultValue;
}
@override
dynamic toJson(num object) {
return object;
}
}
- 2、在pubspec.yaml中配置转换器
# 配置 json_annotation 的全局转换器
json_annotation:
options:
# 启用全局转换器
converters:
- 'package:flutter_module/lib/core/jsonConverter/safe_dateTime_converter.dart::SafeDateTimeConverter'
-
3、在model注解中添加转换器
// 使用 @JsonSerializable 注解标记该类 @JsonSerializable(genericArgumentFactories: true, converters: [SafeNumConverter()]) class Person { // 类的属性 final String name; final num age; // 构造函数 Person(this.name, this.age); // 工厂方法,用于从 JSON 数据反序列化对象 factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json); // 方法,用于将对象序列化为 JSON 数据 Map<String, dynamic> toJson() => _$PersonToJson(this); } -
4、重新执行生成命令
flutter pub run build_runner build --delete-conflicting-outputs
这个时候我们json转model就不会失败了。
扩展
我这边定义了几个转换器,方便拿来即食
SafeMapConverter
import 'dart:convert';
import 'package:json_annotation/json_annotation.dart';
class SafeMapConverter extends JsonConverter<Map<String, dynamic>, dynamic> {
const SafeMapConverter();
@override
Map<String, dynamic> fromJson(dynamic json) {
if (json is Map<String, dynamic>) {
return json;
}
try {
if (json is String) {
final decoded = jsonDecode(json);
if (decoded is Map<String, dynamic>) {
return decoded;
}
}
} catch (e) {
// 处理解析异常
}
return {};
}
@override
dynamic toJson(Map<String, dynamic> object) {
return object;
}
}
SafeListConverter
import 'dart:convert';
import 'package:json_annotation/json_annotation.dart';
class SafeListConverter extends JsonConverter<List<dynamic>, dynamic> {
const SafeListConverter();
@override
List<dynamic> fromJson(dynamic json) {
if (json is List<dynamic>) {
return json;
}
try {
if (json is String) {
final decoded = jsonDecode(json);
if (decoded is List<dynamic>) {
return decoded;
}
}
} catch (e) {
// 捕获解析过程中可能出现的异常
}
return [];
}
@override
dynamic toJson(List<dynamic> object) {
return object;
}
}
SafeDateTimeConverter
class SafeDateTimeConverter implements JsonConverter<DateTime?, dynamic> {
const SafeDateTimeConverter();
@override
DateTime? fromJson(dynamic json) {
if (json is DateTime) return json;
if (json is String) return DateTime.tryParse(json);
if (json is int) return DateTime.fromMillisecondsSinceEpoch(json);
return null;
}
@override
dynamic toJson(DateTime? date) => date?.toIso8601String();
}
JsonTypeAdapter
class JsonTypeAdapter {
// 安全转换数字(处理int/double/字符串数字混合场景)
static num? safeParseNumber(dynamic value) {
if (value is num) return value;
if (value is String) {
return num.tryParse(value);
}
return null;
}
// 安全日期解析(支持多种日期格式)
static DateTime? safeParseDate(dynamic value) {
if (value is DateTime) return value;
if (value is String) return DateTime.tryParse(value);
if (value is int) return DateTime.fromMillisecondsSinceEpoch(value);
return null;
}
// 布尔类型安全转换
static bool safeParseBool(dynamic value) {
if (value is bool) return value;
if (value is int) return value != 0;
if (value is String) {
return value.toLowerCase() == 'true';
}
return false;
}
}