Flutter中高级JSON处理:使用json_serializable进行深入定制
- 文章信息 - Author: 李俊才 (jcLee95)
Visit me at: https://jclee95.blog.csdn.net
Email: 291148484@163.com.
Shenzhen China
Address of this article:https://blog.csdn.net/qq_28550263/article/details/136319144 HuaWei: https://bbs.huaweicloud.com/blogs/422633
json_serializable库:https://pub.dev/packages/json_serializable
【介绍】:在Flutter开发中,处理JSON数据是常见的需求。本文将深入探讨如何使用
json_serializable
包进行高级JSON处理,包括自定义序列化和反序列化的各种技巧和策略。
目 录
- 1. 概述
- 2. 基础设置
- 2.1 添加依赖项
- 如何添加依赖
- 配置完成后
- 2.2 创建模型类
- 2.3 生成代码
- 2.3.1 步骤一:配置 `build.yaml`
- 2.3.1 步骤二:运行 `build_runner`
- 常见问题和解决方案
- 问题:无法生成代码或报错
- 问题:代码生成了,但是出现了意料之外的错误
- 3. 自定义序列化和反序列化
- 4 处理复杂嵌套结构
- 4.1 定义模型类
- 4.2 自定义序列化逻辑
- 4.3 使JsonConverter
- 4.3.1 创建自定义转换器
- 示例:日期时间转换器
- 示例:枚举类型转换器
- 4.3.2 使用自定义转换器
- 应用于字段
- 应用于全局模型
- 4.4 泛型支持
- 4.4.1 genericArgumentFactories参数的作用
- 4.4.2 如何使用genericArgumentFactories处理泛型类型数据
- 步骤一:在模型类上启用genericArgumentFactories
- 步骤二:为泛型类型提供转换函数
- 5. 性能优化和最佳实践
- 6. 结论
1. 概述 1.1 引言
在实际应用开发中,JSON数据格式扮演着至关重要的角色。特别是在Flutter中,JSON被广泛应用于数据交换和应用内部数据处理的各个场景。由于Flutter应用通常需要与后端API进行数据通信,并且需要将接收到的数据转换为Dart对象进行处理,因此对JSON数据的处理成为了开发过程中的常见需求。
在处理JSON数据时,一个强大的工具是json_serializable
包。该包提供了一种自动生成JSON序列化和反序列化代码的方法,可以大大简化开发者的工作。通过使用json_serializable
,开发者可以避免手动编写大量的样板代码,提高开发效率,同时确保生成的代码类型安全且易于维护。
本文旨在深入探讨如何使用json_serializable
包进行高效的JSON处理,涵盖了从基础设置到高级技巧的全面指南。读者可以期待学习到如何利用该工具来自定义序列化和反序列化过程,处理复杂的数据结构,优化性能,并在大型项目中有效管理JSON模型类。
本文适合具有一定Flutter开发经验,希望进一步提升JSON处理技能的开发者。通过学习本文,读者将能够掌握json_serializable
的高级用法,从而更加轻松地处理Flutter项目中的复杂JSON数据。
在Flutter项目中使用json_serializable
进行高级JSON处理前,首先需要在项目的pubspec.yaml
文件中添加几个关键的依赖项:json_serializable
、json_annotation
,以及build_runner
。这些依赖项各自扮演着不同的角色,共同协作,以简化和自动化JSON的序列化和反序列化过程。
json_annotation: 这个包提供了注解(例如**@JsonSerializable**),这些注解用于标记Dart模型类,以便json_serializable
能够识别并自动生成序列化代码。
json_serializable
: 主要的代码生成库,它根据json_annotation
包提供的注解自动生成序列化和反序列化逻辑。
build_runner: 一个构建系统,用于在Flutter项目中执行代码生成。json_serializable
依赖于此来生成相应的.g.dart
文件。
如何添加依赖
- 打开Flutter项目的pubspec.yaml文件。
- 在dependencies部分添加json_annotation,这是运行时依赖。
- 在dev_dependencies部分添加
json_serializable
和build_runner,这些只在开发时使用,不会增加最终应用的体积。
dependencies:flutter:sdk: flutterjson_annotation: ^4.8.1 # 请检查最新版本dev_dependencies:flutter_test:sdk: flutterjson_serializable: ^6.7.1 # 请检查最新版本build_runner: ^2.2.0 # 请检查最新版本
注意:版本号可能会随时间变化,请访问 pub.dev 查找最新版本。
配置完成后
配置完成后,需要运行flutter pub get
命令来获取和安装这些新添加的依赖项。这一步是必须的,因为它会更新项目,使新添加的包可用。
通过这些步骤,你的Flutter项目现在已经准备好使用json_serializable
包进行高级JSON处理了。接下来,可以定义模型类,并使用json_annotation
提供的注解来标记这些类,为自动生成序列化代码做好准备。
在Flutter应用中处理JSON数据时,首先需要定义与JSON结构相对应的Dart模型类。这些模型类不仅使得数据处理更加类型安全和直观,而且是使用json_serializable
进行自动序列化和反序列化的基础。
假设我们有一个表示用户信息的JSON对象,包含用户的姓名、年龄和是否为会员的信息。以下是如何定义一个对应的Dart模型类:
import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart';()
class User {final String name;final int age;final bool isMember;User({required this.name, required this.age, required this.isMember});// 从JSON创建User实例的工厂方法factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);// 将User实例转换为JSON的方法Map<String, dynamic> toJson() => _$UserToJson(this);}
在上面的代码中,@JsonSerializable()注解告诉json_serializable
这个类需要生成序列化代码。part 'user.g.dart';
指令是告诉Dart编译器有一个部分文件user.g.dart
,这个文件将由json_serializable
自动生成,包含fromJson和toJson方法的实现。
json_annotation
包提供了多种注解来自定义序列化行为。最常用的注解是 @JsonSerializable,它可以应用于类级别,用于指示json_serializable
为该类生成序列化逻辑。此外,@JsonKey注解可以应用于类的字段,用于指定如何处理特定的字段,例如自定义字段名、是否包含空值等。
在定义模型类时,可以直接使用Dart的基本数据类型(如String
、int
、boo
l等)来对应JSON中的数据类型。json_serializable
能够自动处理这些基本类型的序列化和反序列化。对于更复杂的数据类型(如日期时间、枚举等),可能需要使用 @JsonKey注解和自定义的转换器来处理。
通过以上步骤,我们定义了一个简单的模型类User,并通过json_annotation
包为其添加了必要的注解,为使用json_serializable
进行序列化处理做好了准备。这个过程是使用json_serializable
包进行高级JSON处理的基础,接下来我们将学习如何生成序列化代码,以及如何自定义序列化和反序列化的行为
在这一节中,我们将详细介绍如何使用 build_runner
命令行工具生成我们的序列化代码。这一步骤是整个 JSON 处理过程中至关重要的一环,因为它会根据我们在模型类中的注解生成必要的序列化和反序列化代码。以下是具体的步骤:
build.yaml
虽然大多数情况下不需要手动配置build.yaml
文件,但在一些特殊情况下,如需要对生成的代码进行特殊设置时,可以在项目根目录下创建或修改build.yaml
文件。例如,你可以指定生成代码的具体参数,如是否包含fromJson和toJson方法的具体实现等。
在项目根目录下创建或修改 build.yaml
文件,添加以下配置:
targets:$default:builders:json_serializable:options:# 指定生成代码的配置# 是否要在生成的代码中包含 fromJson 和 toJson 方法的方法签名# 默认情况下,此值为false,设置为true可以生成方法签名,便于调试generate_to_json: truegenerate_from_json: true
这里我们指定了 generate_to_json
和 generate_from_json
参数,以告知 json_serializable
包在生成的代码中包含 fromJson
和 toJson
方法的方法签名,这对于调试来说非常有用。
请注意,这一步通常是可选的,只有在需要对代码生成过程进行特殊配置时才需要。
2.3.1 步骤二:运行build_runner
在终端中运行以下命令:
flutter pub run build_runner build
这个命令会触发build_runner
去分析项目中的代码,并根据模型类中的注解生成相应的序列化和反序列化代码。生成的代码将保存在与模型类相同目录下的.g.dart
文件中。
如果你希望在代码有变动时自动重新生成序列化代码,可以使用以下命令:
flutter pub run build_runner watch
这将启动一个监听器,当源文件发生变化时,自动重新生成代码。
常见问题和解决方案
在运行 build_runner
过程中,可能会遇到一些常见的问题。
问题:无法生成代码或报错
解决方案:首先,确保你的模型类中的注解正确无误,并且pubspec.yaml
文件中已经正确添加了json_serializable
和build_runner
依赖。其次,检查是否有语法错误或其他编译错误阻止了代码的正常生成。
问题:代码生成了,但是出现了意料之外的错误
解决方案:检查模型类的字段和注解是否符合json_serializable
的要求。确保模型类中没有使用不支持的类型(如直接使用DateTime类型而不是通过自定义转换器)。如果问题依旧,尝试清除之前的生成文件并重新生成:
flutter pub run build_runner clean
flutter pub run build_runner build
通过遵循上述步骤,你应该能够成功生成所需的序列化和反序列化代码,从而在Flutter项目中高效地处理JSON数据。如果在生成代码的过程中遇到任何问题,不妨回顾一下模型类的定义和注解配置,确保一切设置正确无误。
3. 自定义序列化和反序列化在Flutter应用中处理JSON数据时,json_serializable
提供了强大的自定义序列化和反序列化能力。通过使用JsonSerializable注解的不同参数,开发者可以精细控制序列化和反序列化的行为,以满足各种复杂的需求。本节将深入探讨这些参数及其应用。
JsonSerializable注解提供了多个参数,允许开发者自定义序列化和反序列化的过程。
const JsonSerializable({this.anyMap, // 是否允许使用 Map<String, dynamic> 类型的参数,用于接收不在模型中定义的额外字段this.checked, // 是否在序列化和反序列化时验证输入数据的类型this.constructor, // 是否在生成的代码中包含默认构造函数this.createFieldMap, // 是否为每个类的字段创建一个映射,用于确定 JSON 键和类字段之间的关系this.createFactory, // 是否生成一个工厂方法,用于从 JSON 创建对象this.createToJson, // 是否生成 toJson() 方法,用于将对象转换为 JSON 格式this.disallowUnrecognizedKeys, // 是否禁止在反序列化时接受不被识别的键this.explicitToJson, // 是否显式声明 toJson() 方法this.fieldRename, // 用于指定如何重命名类字段的选项this.ignoreUnannotated, // 是否忽略未标记为 @JsonKey 的字段this.includeIfNull, // 是否在生成的 JSON 中包含 null 值的字段this.converters, // 自定义类型转换器this.genericArgumentFactories, // 是否生成用于泛型参数的工厂方法this.createPerFieldToJson, // 是否为每个字段生成单独的 toJson() 方法
});
以下是一些最常用参数的详细描述:
参数名称 | 类型 | 默认值 | 描述 |
---|---|---|---|
anyMap | bool | false | 当设置为true时,允许fromJson方法接受任何类型的Map,而不仅仅是Map<String, dynamic>。这在处理不确定键类型的JSON数据时非常有用。 |
checked | bool | false | 启用类型检查。当设置为true时,序列化和反序列化过程中将验证数据类型。如果类型不匹配,将抛出CheckedFromJsonException异常。这对于开发阶段的错误检测非常有帮助。 |
constructor | String | “” | 指定用于反序列化的构造函数名称。这在类有多个构造函数,或者需要使用非默认构造函数时非常有用。 |
createFactory | bool | true | 控制是否生成fromJson工厂方法。在某些情况下,如果你想自定义反序列化逻辑,可以将此设置为false。 |
createFieldMap | bool | false | 当设置为true时,生成一个字段名称映射表。这对于需要在运行时访问字段名称的情况非常有用。 |
ignoreUnannotated | bool | false | 当设置为true时,只有明确使用@JsonKey注解的字段才会被序列化或反序列化。这可以用来忽略模型中的某些字段。 |
includeIfNull | bool | true | 控制当字段值为null时,是否应该包含在序列化输出中。设置为false可以减少生成的JSON数据的大小。 |
converters | List | [] | 允许指定一组自定义转换器,用于在序列化和反序列化过程中转换特定的数据类型。这对于处理特殊数据类型(如DateTime)非常有用。 |
genericArgumentFactories | bool | false | 当设置为true 时,为包含泛型类型的模型生成额外的工厂参数。这对于处理泛型集合或自定义泛型类型非常重要。 |
3.2 使用场景和示例
在本节中,我们将通过具体的代码示例探讨如何利用json_serializable
的高级特性来自定义序列化和反序列化的过程。这些示例将涵盖不同的使用场景,帮助开发者理解如何根据特定需求选择和使用JsonSerializable构造函数的参数。
场景一:处理带有非标准日期格式的JSON
假设后端API返回的JSON中包含一个日期字段,但该字段的格式并非ISO标准格式,而是"dd-MM-yyyy"格式。我们可以通过自定义转换器来处理这种情况。
首先,定义一个日期转换器:
// 自定义日期转换器类,用于将 DateTime 对象与字符串之间进行 JSON 转换。
class DateConverter implements JsonConverter<DateTime, String> {const DateConverter();/// 将 JSON 字符串转换为 DateTime 对象。////// 参数 [json] 是要转换的 JSON 字符串。/// 返回值是转换后的 DateTime 对象。DateTime fromJson(String json) {return DateFormat('dd-MM-yyyy').parse(json);}/// 将 DateTime 对象转换为 JSON 字符串。////// 参数 [object] 是要转换的 DateTime 对象。/// 返回值是转换后的 JSON 字符串。String toJson(DateTime object) {return DateFormat('dd-MM-yyyy').format(object);}
}
然后,在模型类中使用这个转换器:
// 导入 JSON 序列化/反序列化相关的库。
import 'package:json_annotation/json_annotation.dart';
// 导入自定义的日期转换器。
import 'date_converter.dart'; part 'user.g.dart'; // 导入自动生成的代码文件。()class User {final String name;final int age;()final DateTime birthDate;// 用户类的构造函数,用于创建用户对象。User({required this.name, required this.age, required this.birthDate});// 从 JSON 数据创建用户对象的工厂方法。factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);// 将用户对象转换为 JSON 数据。Map<String, dynamic> toJson() => _$UserToJson(this);}
场景二:忽略未注解的字段
在某些情况下,我们可能希望只序列化和反序列化模型中特定的字段,而忽略其他未明确标注的字段。这可以通过设置ignoreUnannotated参数为true
来实现。
// 使用@JsonSerializable注解,指定在序列化/反序列化时忽略未注解的字段。
(ignoreUnannotated: true)class User {// 用户名,使用@JsonKey注解指定在 JSON 中的名称为'user_name'。(name: 'user_name')final String name;// 用户年龄,未使用@JsonKey注解的字段将被忽略。final int age;// 用户类的构造函数,用于创建用户对象。User({required this.name, this.age});// 从 JSON 数据创建用户对象的工厂方法。factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);// 将用户对象转换为 JSON 数据。Map<String, dynamic> toJson() => _$UserToJson(this);}
场景三:处理具有泛型的数据结构
当我们的模型类包含泛型字段时,例如一个List<T>
,我们可以使用genericArgumentFactories
参数来确保类型安全的序列化和反序列化。
首先,定义一个泛型模型类:
// 使用@JsonSerializable注解,启用泛型参数工厂方法。
(genericArgumentFactories: true)class Response<T> {/// 数据字段,用于存储响应数据。final T data;/// 构造函数,用于创建响应对象。Response({required this.data});/// 从 JSON 数据创建响应对象的工厂方法。////// 参数 [json] 是要转换的 JSON 数据。/// 参数 [fromJsonT] 是用于从 JSON 数据创建泛型类型的工厂方法。factory Response.fromJson(Map<String, dynamic> json, T Function(Object? json) fromJsonT) =>_$ResponseFromJson(json, fromJsonT);/// 将响应对象转换为 JSON 数据。////// 参数 [toJsonT] 是用于将泛型类型转换为 JSON 数据的方法。Map<String, dynamic> toJson(Object Function(T value) toJsonT) =>_$ResponseToJson(this, toJsonT);}
然后,使用时提供具体类型的转换方法:
final response = Response<User>.fromJson(json, User.fromJson);
通过这些示例,我们可以看到json_serializable
提供的参数和特性如何使我们能够处理各种复杂和特殊的序列化和反序列化场景。通过合理利用这些高级特性,开发者可以在Flutter项目中更加灵活和高效地处理JSON数据。
4 处理复杂嵌套结构
在处理JSON数据时,我们经常会遇到复杂的嵌套结构,例如一个对象内嵌套另一个对象,或者对象内包含数组等。这些结构在实际的应用场景中非常常见,比如用户信息中包含地址信息,地址信息本身又是一个包含多个字段的对象。正确处理这些嵌套结构对于确保数据的正确序列化和反序列化至关重要。
4.1 定义模型类
首先,我们需要为每一个嵌套的结构定义一个模型类。这样做不仅有助于保持代码的清晰和组织性,还能利用json_serializable
自动生成序列化代码的优势。
假设我们有以下JSON结构:
{"name": "Jack Lee","email": "291148484@163.com","address": {"street": "123 Main St","city": "Anytown","zipCode": "12345"}
}
为了处理这个结构,我们可以定义两个模型类:User和Address。
import 'package:json_annotation/json_annotation.dart';part 'user.g.dart';
part 'address.g.dart';// 使用@JsonSerializable注解的用户类,表示用户对象。
()
class User {final String name;final String email;final Address address;// 构造函数,接受用户名、电子邮件地址和地址对象User({required this.name, required this.email, required this.address});// 从JSON映射转换为User对象的工厂方法factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);// 将User对象转换为JSON映射的方法Map<String, dynamic> toJson() => _$UserToJson(this);
}// 使用@JsonSerializable注解的地址类,表示地址对象。
()
class Address {final String street;final String city;final String zipCode;Address({required this.street, required this.city, required this.zipCode});/// 从JSON映射转换为Address对象的工厂方法factory Address.fromJson(Map<String, dynamic> json) => _$AddressFromJson(json);/// 将Address对象转换为JSON映射的方法Map<String, dynamic> toJson() => _$AddressToJson(this);
}
在这个例子中,User类包含了一个Address类型的字段。通过这种方式,我们可以很容易地处理嵌套的JSON结构。
4.2 自定义序列化逻辑
虽然json_serializable
能够自动处理许多序列化和反序列化的场景,但有时我们可能需要对某些字段进行特殊处理。在这种情况下,我们可以使用JsonKey注解来自定义序列化逻辑。
例如,如果我们想在序列化User对象时将email字段转换为小写,我们可以这样做:
()
class User {final String name;(fromJson: _emailFromJson, toJson: _emailToJson)final String email;final Address address;User({required this.name, required this.email, required this.address});factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);Map<String, dynamic> toJson() => _$UserToJson(this);// 自定义fromJson逻辑static String _emailFromJson(String email) => email;// 自定义toJson逻辑static String _emailToJson(String email) => email.toLowerCase();
}
在这个例子中,我们通过**@JsonKey注解的fromJson和toJson**参数指定了自定义的序列化和反序列化逻辑。这种方法非常灵活,可以用来处理各种特殊情况。
通过定义模型类和必要时自定义序列化逻辑,我们可以有效地处理复杂的嵌套JSON结构。这不仅使得代码更加清晰和易于维护,还能利用json_serializable
提供的强大功能来简化开发过程。
4.3 使JsonConverter
在Flutter应用中处理JSON数据时,我们经常会遇到需要将特殊数据类型(如日期时间、枚举类型等)从JSON格式转换为Dart对象,或者反之。json_serializable
提供了一个强大的工具JsonConverter,允许我们自定义这些转换过程。本节将介绍如何创建和使用JsonConverter来处理这些特殊的数据类型转换。
4.3.1 创建自定义转换器
要创建一个自定义转换器,我们需要定义一个类并实现JsonConverter接口。这个接口要求我们提供两个方法:fromJson和toJson,分别用于处理从JSON到Dart的转换和从Dart到JSON的转换。
示例:日期时间转换器
假设我们的JSON数据中包含ISO 8601格式的日期字符串,我们想将这些字符串转换为Dart的DateTime对象。以下是如何定义一个日期时间转换器:
import 'package:json_annotation/json_annotation.dart';// 定义一个JsonConverter实现类,用于转换DateTime数据类型。
class DateTimeConverter implements JsonConverter<DateTime, String> {const DateTimeConverter();// 从JSON到Dart的转换:将日期字符串转换为DateTime对象。DateTime fromJson(String json) => DateTime.parse(json);// 从Dart到JSON的转换:将DateTime对象转换为日期字符串。String toJson(DateTime object) => object.toIso8601String();
}
示例:枚举类型转换器
对于枚举类型,我们通常需要将JSON中的字符串转换为Dart枚举值。以下是一个枚举类型转换器的示例:
import 'package:json_annotation/json_annotation.dart';enum Status { active, inactive }// 定义一个JsonConverter实现类,用于转换Status枚举类型。
class StatusConverter implements JsonConverter<Status, String> {const StatusConverter();// 从JSON到Dart的转换:将字符串转换为Status枚举值。Status fromJson(String json) {switch (json) {case 'active':return Status.active;case 'inactive':return Status.inactive;default:throw ArgumentError('Unknown status: $json');}}// 从Dart到JSON的转换:将Status枚举值转换为字符串。String toJson(Status object) => object.toString().split('.').last;
}
4.3.2 使用自定义转换器
定义了自定义转换器后,我们可以通过在模型类或字段上应用@JsonConverter注解来使用它们。
应用于字段
如果只有特定的字段需要使用转换器,我们可以直接在该字段上应用@JsonConverter注解:
import 'package:json_annotation/json_annotation.dart';part 'user.g.dart';/// 使用@JsonSerializable注解的用户类,表示用户对象。
()
class User {final String name;// 用户创建时间,使用DateTimeConverter进行转换()final DateTime createdAt;// 用户状态,使用StatusConverter进行转换()final Status status;// 构造函数,接受用户名、创建时间和状态User({required this.name, required this.createdAt, required this.status});// 从JSON映射转换为User对象的工厂方法factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);/// 将User对象转换为JSON映射的方法Map<String, dynamic> toJson() => _$UserToJson(this);
}
应用于全局模型
如果我们希望在整个模型类中使用同一个转换器,可以在类级别应用**@JsonSerializable**注解,并通过converters
参数指定转换器列表:
(converters: [DateTimeConverter(), StatusConverter()])
class User {final String name;final DateTime createdAt;final Status status;User({required this.name, required this.createdAt, required this.status});factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);Map<String, dynamic> toJson() => _$UserToJson(this);
}
通过创建和使用自定义的JsonConverter,我们可以灵活地处理JSON数据中的特殊数据类型,使得序列化和反序列化过程更加精确和高效。
4.4 泛型支持
在Flutter应用中处理JSON数据时,我们经常会遇到需要处理包含泛型类型数据的模型类。这在处理如列表(List<T>
)、映射(Map<K, V>
)或任何包含泛型参数的自定义数据类型时尤其常见。json_serializable
通过提供genericArgumentFactories
参数支持泛型类型的序列化和反序列化,使得开发者能够以类型安全的方式处理这些复杂的数据结构。
4.4.1 genericArgumentFactories参数的作用
genericArgumentFactories
参数允许开发者为包含泛型类型的模型类生成额外的工厂参数。这些工厂参数用于在序列化和反序列化过程中转换泛型类型的数据,确保类型的正确性和安全性。
当设置genericArgumentFactories
为true
时,json_serializable
会为模型类的fromJson和toJson方法生成额外的参数。这些参数是函数,用于处理泛型类型的数据转换,使得开发者可以自定义泛型类型的序列化和反序列化逻辑。
4.4.2 如何使用genericArgumentFactories处理泛型类型数据
为了使用genericArgumentFactories
处理包含泛型类型数据的模型类,我们需要遵循以下步骤:
步骤一:在模型类上启用genericArgumentFactories
首先,在模型类的**@JsonSerializable**注解中设置genericArgumentFactories
为true
。这告诉json_serializable
为这个模型类生成支持泛型的序列化和反序列化代码。
import 'package:json_annotation/json_annotation.dart';part 'response.g.dart';// 使用@JsonSerializable注解的类,用于表示通用的响应对象。
(genericArgumentFactories: true)
class Response<T> {// 数据对象final T data;// 接受数据对象Response({required this.data});// 从JSON映射转换为Response对象的工厂方法factory Response.fromJson(Map<String, dynamic> json, T Function(Object? json) fromJsonT) =>_$ResponseFromJson(json, fromJsonT);// 将Response对象转换为JSON映射的方法Map<String, dynamic> toJson(Object? Function(T value) toJsonT) =>_$ResponseToJson(this, toJsonT);
}
步骤二:为泛型类型提供转换函数
在使用模型类进行序列化或反序列化时,需要为泛型类型T提供相应的转换函数。这些函数负责将泛型类型的数据从JSON转换为Dart对象,或者反之。
// 假设我们有一个User类,我们想将其作为Response的泛型类型
User userFromJson(Object? json) => User.fromJson(json as Map<String, dynamic>);
Object? userToJson(User user) => user.toJson();// 反序列化示例
Map<String, dynamic> userJson = ...; // 从API获取的JSON数据
var response = Response<User>.fromJson(userJson, userFromJson);// 序列化示例
Map<String, dynamic> json = response.toJson(userToJson);
通过这种方式,我们可以灵活地处理包含泛型类型数据的模型类,无论泛型类型是基本数据类型、自定义对象还是其他复杂的数据结构。genericArgumentFactories
参数提供了一种类型安全的方法来处理泛型数据,极大地增强了json_serializable
在处理复杂JSON数据时的能力和灵活性。
5. 性能优化和最佳实践
在使用json_serializable
进行JSON序列化和反序列化时,性能优化是一个重要的考虑因素。正确地使用一些关键特性可以提高开发效率,同时确保应用在生产环境中运行得更快、更稳定。本节将探讨如何通过使用checked
参数来提高开发效率,以及其他一些性能优化和最佳实践。
5.1 何时使用checked以提高开发效率
json_serializable
提供了一个checked
参数,当设置为true
时,可以在序列化和反序列化过程中增加类型检查。这意味着如果JSON数据类型与模型类不匹配,将抛出CheckedFromJsonException异常,从而帮助开发者快速定位和修复问题。
5.1.1 开发阶段的优势
在开发阶段,使用checked
参数的优势非常明显。它可以帮助开发者及早发现数据类型不匹配的问题,避免这些问题在后期或生产环境中引发更严重的错误。通过即时的反馈,开发者可以快速调整和修正数据模型,提高开发效率。
5.1.2 生产环境的调整
尽管在开发阶段使用checked
参数带来了明显的好处,但在生产环境中,过多的类型检查可能会对性能产生负面影响。因此,建议在生产环境中关闭checked
参数,以优化性能。这可以通过条件编译或配置文件来实现,确保在生产构建中不启用类型检查。
5.1.3 实践建议
开发阶段:在**@JsonSerializable**注解中设置checked: true
,以便在开发过程中利用类型检查来发现和修复问题。
生产环境:通过条件编译或其他机制,确保在生产构建中关闭checked
参数。例如,可以在构建脚本中根据构建类型(开发或生产)动态设置此参数。
通过合理使用checked
参数,开发者可以在不牺牲性能的情况下,提高开发效率并减少生产环境中的错误。这是json_serializable
在实际应用中的一个重要性能优化和最佳实践。
5.2 选择合适的构造函数以优化性能
在使用json_serializable
进行JSON序列化和反序列化时,选择合适的构造函数对于优化性能至关重要。构造函数的选择不仅影响代码的可读性和易用性,还直接关系到序列化和反序列化过程的效率。
5.2.1 默认构造函数与命名构造函数
Dart提供了默认构造函数和命名构造函数两种方式。在使用json_serializable
时,通常会利用工厂构造函数fromJson来从JSON创建对象实例,而toJson方法则用于将对象实例转换回JSON。
默认构造函数:简单直观,适用于大多数情况,尤其是当模型类的字段与JSON结构高度匹配时。
命名构造函数:提供了更多的灵活性,允许在实例化对象时执行额外的逻辑,如字段验证、转换等。这在处理复杂的JSON结构或需要额外处理逻辑时非常有用。
5.2.2 性能影响
初始化开销:使用命名构造函数或包含额外逻辑的工厂构造函数可能会增加初始化对象的开销。虽然这种开销在单个实例化操作中可能微不足道,但在处理大量数据时,这些开销累积起来可能会影响整体性能。
代码生成与优化:json_serializable
通过代码生成来实现序列化和反序列化逻辑,合理选择构造函数可以减少生成的代码量,从而提高编译后代码的执行效率。
5.2.3 实践建议
简单场景优先使用默认构造函数:对于简单的模型类,优先使用默认构造函数,减少不必要的复杂性,提高代码的清晰度和执行效率。
复杂场景考虑命名构造函数:面对复杂的JSON结构或需要额外处理逻辑时,可以考虑使用命名构造函数或工厂构造函数,通过在构造过程中加入必要的逻辑来保证数据的正确性和完整性。
性能测试:在决定使用哪种构造函数之前,进行性能测试,比较不同实现方式对性能的影响,确保在满足功能需求的同时,也能达到较好的性能表现。
通过根据模型类的功能需求的同时,也能达到较好的性能表现。
通过根据模型类的具体需求仔细选择构造函数,开发者可以在保证代码质量和可维护性的同时,优化应用的性能。
5.3 管理大型项目中的JSON模型
在大型Flutter项目中,随着业务的发展,可能会涉及到大量的JSON模型类。有效管理这些模型类是确保项目可维护性和扩展性的关键。接下来是一些管理大型项目中JSON模型的策略和建议。
5.3.1 模块化和重用
模块化设计:将相关的模型类组织到相应的模块或包中,按照功能或业务逻辑进行划分。这样不仅有助于代码的组织和管理,也便于团队协作和模块间的解耦。
重用和继承:在定义模型类时,应考虑重用现有的模型或通过继承来扩展新的模型。这可以减少重复代码,提高代码的复用率。
5.3.2 版本控制和变更管理
版本控制:使用版本控制系统(如Git)管理模型类的变更,确保每次变更都有记录,便于追踪和回溯。
变更管理:对于模型类的修改,尤其是涉及到与后端接口对接的模型,需要有严格的变更管理流程,包括代码审查、测试验证等,以确保变更不会引入错误或影响现有功能。
5.3.3 文档和注释
文档:为每个模型类和其字段编写清晰的文档,说明其用途、数据来源以及与其他模型的关系等。这对于新成员的快速上手以及团队内部的沟通都非常重要。
注释:在模型类及其字段上添加必要的注释,特别是对于一些复杂的逻辑或特殊的处理方式,注释可以帮助其他开发者理解代码的意图。
5.3.4 自动化测试
单元测试:为重要的模型类编写单元测试,尤其是那些包含自定义序列化逻辑的模型。这可以确保模型的序列化和反序列化行为符合预期,同时也是对模型正确性的验证。
集成测试:进行集成测试以验证模型类与后端接口的交互是否正确,特别是在后端接口发生变更时,及时发现并修复问题。
通过实施这些策略和建议,开发者可以有效地管理和维护大型项目中的JSON模型类,确保项目的健壯性和可持续发展。
6. 结论
通过本文的介绍,开发者应该能够掌握json_serializable
的高级用法,有效地处理Flutter项目中的复杂JSON数据。无论是基础的模型类定义,还是高级的自定义序列化逻辑,json_serializable
都提供了强大而灵活的工具,帮助开发者提高开发效率,同时保证代码的质量和可维护性。鼓励读者在实际项目中实验和探索更多自定义选项,找到最适合自己项目需求的序列化策略。
相关文章:

Flutter中高级JSON处理:使用json_serializable进行深入定制
Flutter中高级JSON处理 使用json_serializable库进行深入定制 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at: https://jclee95.blog.csdn.netEmail: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/qq_28550263/article/details/1363…...
华为OD技术面试案例4-2024年
个人情况:985本,目标院校非计算机专业,情况比较特殊,23年11月研究生退学,电子信息类专业。 初识od:10月底打算退学的时候在智联、BOSS上疯狂投硬件方面的岗位。投了大概一两天后有德科和HW的HR打电话给我介…...
【TestNG】(4) 重试机制与监听器的使用
在UI自动化测试用例执行过程中,经常会有很多不确定的因素导致用例执行失败,比如网络原因、环境问题等,所以我们有必要引入重试机制(失败重跑),来提高测试用例成功率。 在不写代码的情况没有提供可配置方式…...

“智农”-高标准农田
高标准农田是指通过土地整治、土壤改良、水利设施、农电配套、机械化作业等措施,提升农田质量和生产能力,达到田块平整、集中连片、设施完善、节水高效、宜机作业、土壤肥沃、生态友好、抗灾能力强、与现代农业生产和经营方式相适应的旱涝保收、稳产高产…...
利用 lxml 库的XPath()方法在网页中快速查找元素
XPath() 函数是 lxml 库中 Element 对象的方法。在使用 lxml 库解析 HTML 或 XML 文档时,您可以通过创建 Element 对象来表示文档的元素,然后使用 Element 对象的 XPath() 方法来执行 XPath 表达式并选择相应的元素。 具体而言,XPath() 方法是…...

nginx---------------重写功能 防盗链 反向代理 (五)
一、重写功能 rewrite Nginx服务器利用 ngx_http_rewrite_module 模块解析和处理rewrite请求,此功能依靠 PCRE(perl compatible regular expression),因此编译之前要安装PCRE库,rewrite是nginx服务器的重要功能之一,重写功能(…...

unity shaderGraph实例-物体线框显示
文章目录 本项目基于URP实现一,读取UV网格,由自定义shader实现效果优缺点效果展示模型准备整体结构各区域内容区域1区域2区域3区域4shader属性颜色属性材质属性后处理 实现二,直接使用纹理,使用默认shader实现优缺点贴图准备材质准…...

分类问题经典算法 | 二分类问题 | Logistic回归:公式推导
目录 一. Logistic回归的思想1. 分类任务思想2. Logistic回归思想 二. Logistic回归算法:线性可分推导 一. Logistic回归的思想 1. 分类任务思想 分类问题通常可以分为二分类,多分类任务;而对于不同的分类任务,训练的主要目标是…...

redis实现分布式全局唯一id
目录 一、前言二、如何通过Redis设计一个分布式全局唯一ID生成工具2.1 使用 Redis 计数器实现2.2 使用 Redis Hash结构实现 三、通过代码实现分布式全局唯一ID工具3.1 导入依赖配置3.2 配置yml文件3.3 序列化配置3.4 编写获取工具3.5 测试获取工具 四、运行结果 一、前言 在很…...

Sora引发安全新挑战
文章目录 前言一、如何看待Sora二、Sora加剧“深度伪造”忧虑三、Sora无法区分对错四、滥用导致的安全危机五、Sora面临的安全挑战总结前言 今年2月,美国人工智能巨头企业OpenAI再推行业爆款Sora,将之前ChatGPT以图文为主的生成式内容全面扩大到视频领域,引发了全球热议,这…...
Android 14.0 Launcher3定制化之桌面分页横线改成圆点显示功能实现
1.前言 在14.0的系统rom产品定制化开发中,在进行launcher3的定制化中,在双层改为单层的开发中,在原生的分页 是横线,而为了美观就采用了系统原来的另外一种分页方式,就是圆点比较美观,接下来就来分析下相关…...

SemiDrive E3 MCAL 开发系列(3)– Wdg 模块的使用
一、 概述 本文将会介绍 SemiDrive E3 MCAL Wdg 模块的基本配置,并且会结合实际操作的介绍,帮助新手快速了解并掌握这个模块的使用,文中的 MCAL 是基于 PTG3.0 的版本,开发板是官方的 E3640 网关板。 二、 Wdg 模块的主要配置 …...
AI推荐算法的演进之路
推荐算法 基于大数据和AI技术,提供全流程一站式推荐平台,协助企业构建个性化推荐应用,提升企业应用的点击率留存率和永久体验。目前,主要的推荐方法包括:基于内容推荐、协同过滤推荐、基于关联规则推荐、基于效用推荐…...

Tomcat安装,配置文件、组件
一、Tomcat的基本功能 1.1.Tomcat是什么? Tomcat服务器是一个免费的开放源代码的Web应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP程序的首选。一般来说,T…...
精读《React Hooks 最佳实践》
简介 React 16.8 于 2019.2 正式发布,这是一个能提升代码质量和开发效率的特性,笔者就抛砖引玉先列出一些实践点,希望得到大家进一步讨论。 然而需要理解的是,没有一个完美的最佳实践规范,对一个高效团队来说&#x…...

varFormatter 数据格式化库 以性能优先的 快速的 内存对象格式转换
varFormatter 数据格式化 技术 开源技术栏 对象/变量格式化工具库,其支持将一个对象进行按照 JSON XML HTML 等格式进行转换,并获取到结果字符串! 目录 文章目录 varFormatter 数据格式化 技术目录介绍获取方式 使用实例格式化组件的基本使…...

基于PHP的在线英语学习平台
有需要请加文章底部Q哦 可远程调试 基于PHP的在线英语学习平台 一 介绍 此在线英语学习平台基于原生PHP开发,数据库mysql。系统角色分为学生,教师和管理员。(附带参考设计文档) 技术栈:phpmysqlphpstudyvscode 二 功能 学生 1 注册/登录/…...
基于微信小程序电影院订票选座系统 (后台JSP+JDBC+Mysql)答辩常规问题和如何回答(答辩指导)
博主介绍:黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;专注大学生毕业设计教育和辅导。 所有项目都配有从入门到精通的基础知识视频课程ÿ…...
C++知识点总结(22):模拟算法真题 ★★★☆☆《安全警报》
安全警报 1. 审题 题目描述 Z市最大的金融公司:太平洋金融遭到了入侵,一名黑客潜入到了公司中,公司紧急启动安保程序,将大楼封锁,并安排作为安全主管的你对楼层进行搜查。所以你准备写一个程序,输入搜查楼…...

蓝桥杯练习系统(算法训练)ALGO-993 RP大冒险
资源限制 内存限制:64.0MB C/C时间限制:200ms Java时间限制:600ms Python时间限制:1.0s 问题描述 请尽情使用各种各样的函数来测试你的RP吧~~~ 输入格式 一个数N表示测点编号。 输出格式 一个0~9的数。 样例输入 0 样…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...

以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...

Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...

Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...

Golang——9、反射和文件操作
反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一:使用Read()读取文件2.3、方式二:bufio读取文件2.4、方式三:os.ReadFile读取2.5、写…...