文 / Andrew Brogdon
来源 | 谷歌开发者 公众号
在某种程度上,大多数应用都需要与外界互动,并从在线终端地址获取数据。借助 Dart 的 http 包,发出 HTTPS get 请求以获取天气预报或世界杯最终比分变得非常简单:
1 import 'dart:async';
2 import 'package:http/http.dart' as http;
3
4 final response = await http.get(myEndpointUrl);
5 if (response.statusCode == 200) {
6 // use the data in response.body
7 } else {
8 // handle a failed request
9 }
response.body 中的数据可能是 JSON 字符串,而我们还需要完成一些工作,才能将其用于微件。首先,您需要将字符串解析为更易于管理的 JSON 表示形式。然后,您必须将该表示形式转化为模型或其他一些强类型变量,如此一来,您就可以有效地使用这些字符串。
幸运的是,Dart 团队和社群对 JSON 已进行过诸多讨论,并且能够提供解决方案。我会按照复杂性从低到高的顺序介绍三种解决方案,分别是手写构造函数、json_serializable 和 built_value。
使用全部三种方法将数据反序列化的调用非常相似。手写构造函数和 json_serializable 的代码行如下所示:
1 final myObject = SimpleObject.fromJson(json.decode(aJsonString));
built_value 的反序列化调用如下所示:
1 final myObject = serializers.deserializeWith(
2 SimpleObject.serializer, json.decode(aJsonString));
真正的区别是,在该 “SimpleObject” 类中为您生成多少代码,以及这些代码有何作用。
手写构造函数
复杂性最低的方法:不为您生成代码。
您可以做自己想做的任何事,但您必须进行维护。
json_serializable
为您生成 fromJson 构造函数和 toJson 方法。
在构建应用之前,您需要在项目中加入若干个包,并使用 source_gen 生成部分文件。
对所生成的资源进行自定义可能会很棘手。
built_value
为序列化、不可变性、toString 方法、hashCode 属性等生成代码。这是具备诸多功能的重量级解决方案。
与 json_serializable 一样,您需要导入许多包并使用 source_gen。
拥有基于插件的可扩展序列化架构。
对实例创建和可变性等方面有见解。
正如下文所述,您适合使用哪个内容库其实取决于项目详情,特别是项目大小和状态管理方法。对拥有一位维护者的兴趣项目来说,手写构造函数很有帮助,而由庞大分布式团队(他们需要不可变模型来保持逻辑清晰)构建的应用则会真正从 “built_value” 受益。
不过,现在我们还是从头开始介绍:将 JSON 从字符串解析为更方便使用的内存表示形式。无论您之后决定采取哪种方法,相关流程中的这一步都是一样的。
解析 JSON
您可以使用 dart:convert 库将 JSON 字符串转换为中间格式:
1 import 'dart:convert';
2
3 try {
4 final parsed = json.decode(aJsonStr);
5 } on FormatException catch (e) {
6 print("That string didn't look like Json.");
7 } on NoSuchMethodError catch (e) {
8 print('That string was null!');
9 }
如果该字符串包含有效的 JSON,系统会返回对 List 或 Map
1 final dynamicListOfInts = json.decode(aJsonArrayOfInts);
2
3 // Create a strongly typed list with references to the data that are casted
4 // immediately. This is probably the better approach for data model classes.
5 final strongListOfInts = List<int>.from(dynamicListOfInts);
6
7 // Or create a strongly typed list with references that will be lazy-casted
8 // when used.
9 final anotherStrongListOfInts = List<int>.from(dynamicListOfInts);
更复杂的有效负载才是有趣之处。将 Map
手写构造函数
我们必须从某个地方开始,对吗?如果您有一个小应用,而且数据也不是很复杂,那么自行编写采用 Map
1 {
2 "aString": "Blah, blah, blah.",
3 "anInt": 1,
4 "aDouble": 1.0,
5 "aListOfStrings": ["one", "two", "three"],
6 "aListOfInts": [1, 2, 3],
7 "aListOfDoubles": [1.0, 2.0, 3.0]
8 }
匹配类的代码可能如下所示:
1 class SimpleObject {
2 const SimpleObject({
3 this.aString,
4</