Flutter修仙之旅,进度有点慢哈,毕竟修仙之余还得上班,今天给大家介绍Flutter中的Json幻化技巧,助大家修仙一臂之力
Json解析是平时日常开发的一个非常重要的部分,大部分从接口返回的数据都是Json格式,客户端通过解析Json数据来进行UI界面的绘制和展示。
Flutter给开发者提供了一个非常方便的解析库—— dart:convert来帮助开发者进行Json解析的相关操作。
下面,通过梳理平时开发中常用的一些Json数据格式,来一起看下如何使用dart:convert库来进行Json解析。
首先,来了解下dart:convert库的基本使用,首先需要引入这个库,代码如下所示。
1. import 'dart:convert' show json;
import中使用show关键字表示这里只引入一部分库代码,即引入json相关的代码。接下来,直接通过decode函数,传入json_data数据即可解析,解析返回的数据为Map,key为String类型,value为dynamic类型,类似于Java中的Object类型,这点很好理解,因为JsonObject的Value可以为多种类型,例如String、int等等,所以这里只会返回dynamic类型。
1. Map<String, dynamic> decodeJson = json.decode(json_data);
有了返回的Map之后,就可以直接解析Map来获得需要的数据了,这里通过一个Text组件简单的展示返回的数据,整个代码如下所示。
1. import 'dart:convert' show json;
2.
3. import 'package:flutter/material.dart';
4.
5. void main() => runApp(MyApp());
6.
7. class MyApp extends StatelessWidget {
8. @override
9. Widget build(BuildContext context) {
10. return MaterialApp(
11. title: 'Flutter Demo',
12. theme: ThemeData(
13. primarySwatch: Colors.blue,
14. ),
15. home: MyHomePage(),
16. );
17. }
18.}
19.
20.class MyHomePage extends StatefulWidget {
21. @override
22. _MyHomePageState createState() => _MyHomePageState();
23.}
24.
25.class _MyHomePageState extends State<MyHomePage> {
26. @override
27. Widget build(BuildContext context) {
28. return Scaffold(
29. appBar: AppBar(
30. title: Text('Json'),
31. ),
32. body: Column(
33. children: <Widget>[
34. Container(
35. margin: EdgeInsets.all(16),
36. child: FutureBuilder(
37. future: DefaultAssetBundle.of(context)
38. .loadString("assets/basicMap.json"),
39. builder: (context, snapshot) {
40. if (snapshot.hasData) {
41. Map<String, dynamic> decodeJson
42. = json.decode(snapshot.data);
43. return Text(decodeJson.toString());
44. } else {
45. return Text('loading...');
46. }
47. },
48. ),
49. )
50. ],
51. ),
52. );
53. }
54.}
Json数据文件这里放置在Asset中,所以需要通过一个FutureBuilder来做异步加载,DefaultAssetBundle是一个帮助开发者从Asset中读取文件的工具,通过上面这个框架代码,就可以解析Json并展示数据了。
下面是笔者梳理的几种常见的Json数据格式,与在Android中操作Json类似,通常情况下,都会生成一个Json对应的Model来实现对Json的映射,不过在Flutter中,由于不能使用反射,所以不能像Android的Gson那样直接通过Model反射来实现Json-model的序列化。
这个应该是最简单的Json数据格式了,最外层是JsonObject,包含kv类型的Json数据,一个常见的示例如下所示。
1. {
2. "code": 0,
3. "result": "success",
4. "message": "message ok"
5. }
从这个最简单的例子开始,让我们一步步来了解Dart中的Json解析。
首先,与在Android中解析Json一样,创建一个Dart Model来进行映射,代码如下所示。
1. class BasicMap {
2. int code;
3. String result;
4. String message;
5.
6. BasicMap.fromJson(Map<String, dynamic> json)
7. : code = json['code'],
8. result = json['result'],
9. message = json['message'];
10.
11. @override
12. String toString() {
13. return 'BasicModel: code: $code ,result: $result ,message: $message';
14. }
15.}
这个类与在Android中生成的Model非常类似,首先是属性名,其次是构造函数,最后重写了toString来进行展示。这里的重点就在中间的构造函数中,该具名构造函数接受一个Map<String, dynamic>类型的参数,也就是前面提到的通过dart:convert转换出来的数据,并通过构造函数给属性赋值,值就是参数中取出的数据。修改下上面的框架代码,传入json String,就可以直接返回一个Model了,代码如下所示。
1. Map<String, dynamic> decodeJson = json.decode(snapshot.data);
2. BasicMap basicModel = BasicMap.fromJson(decodeJson);
到此为止,Dart中的Json解析和Android中的Json解析基本都是一致的。
第一种格式中,都是基本数据类型,下面再增加一个数组类型的数据,如下所示,key:data的value是一个String数组。
1. {
2. "code": 0,
3. "result": "success",
4. "message": "message ok",
5. "data": [
6. "xuyisheng",
7. "zhujia"
8. ]
9. }
仿照第一个的示例,来生成一个Model,代码如下所示。
1. class BasicMapWithList {
2. int code;
3. String result;
4. String message;
5. List<String> data;
6.
7. BasicMapWithList.fromJson(Map<String, dynamic> json)
8. : code = json['code'],
9. result = json['result'],
10. message = json['message'],
11. data = json['data'];
12.
13. @override
14. String toString() {
15. return 'BasicModel: code: $code ,result: $result ,
16. message: $message ,data: ${data.toString()}';
17. }
18.}
但运行之后,大家可以发现Json并不能正确的解析,并提示了下面的错误。
1. type 'List<dynamic>' is not a subtype of type 'List<String>'
问题就出在对String数组的解析上,data属性的类型是List<String>,但dart:convert解析后返回的是List<dynamic>,不同类型的数组之间是不能相互转换的,所以代码报错了。知道了错误原因后再要解决就非常简单了,只需要指定下返回数据的类型即可,修改下构造函数,代码如下所示。
1. BasicMapWithList.fromJson(Map<String, dynamic> json)
2. : code = json['code'],
3. result = json['result'],
4. message = json['message'],
5. data = List<String>.from(json['data']);
通过List<String>.from()函数,将dynamic类型直接转换成了String类型,这样解析就没有问题了。
前面的Json数据都不包含嵌套,下面给Json数据增加一层嵌套,data的value是一个JsonObject,代码如下所示。
1. {
2. "code": 0,
3. "result": "success",
4. "message": "message ok",
5. "data": {
6. "name": "xuyisheng",
7. "age": 18
8. }
9. }
那么针对嵌套的Json数据,首先要从嵌套的最里层逐步向外创建Model,这点和Gson生成Model非常类似。
因此,首先,需要生成DataModel,代码如下所示。
1. class Data {
2. String name;
3. int age;
4.
5. Data.fromJson(Map<String, dynamic> json)
6. : name = json['name'],
7. age = json['age'];
8.
9. @override
10. String toString() {
11. return 'name: $name ,age: $age';
12. }
13.}
这个非常简单,和第一部分的处理是一样的。
接下来,再来生成外层的Model,代码如下所示。
1. class BasicMapWithModel {
2. int code;
3. String result;
4. String message;
5. Data data;
6.
7. BasicMapWithModel.fromJson(Map<String, dynamic> json)
8. : code = json['code'],
9. result = json['result'],
10. message = json['message'],
11. data = json['data'];
12.
13. @override
14. String toString() {
15. return 'BasicModel: code: $code ,result: $result ,
16. message: $message ,data: ${data.toString()}';
17. }
18.}
运行下代码,你会发现又碰到了类型转换的错误,显然,问题同样出现在data的解析上,json[‘data’]返回的dynamic类型,所以需要转换为Data类型,因此,修改后的代码如下所示。
1. BasicMapWithModel.fromJson(Map<String, dynamic> json)
2. : code = json['code'],
3. result = json['result'],
4. message = json['message'],
5. data = Data.fromJson(json['data']);
通过类型转换,就完成了解析。
在前一种数据格式的基础上,再进行进一步的嵌套,data中是一个JsonArray,数据如下所示。
1. {
2. "code": 0,
3. "result": "success",
4. "message": "message ok",
5. "data": [
6. {
7. "name": "xuyisheng",
8. "age": 18
9. },
10. {
11. "name": "zhujia",
12. "age": 3
13. }
14. ]
15.}
对于这样的Json,处理方式和前面的一种基本类似,首先,从里层生成一个最基本的数据Model,可以随便命名,代码如下所示。
1. class Person {
2. String name;
3. int age;
4.
5. Person.fromJson(Map<String, dynamic> json)
6. : name = json['name'],
7. age = json['age'];
8.
9. @override
10. String toString() {
11. return 'name: $name ,age: $age';
12. }
13.}
再生成外面的数据Model,代码如下所示。
1. class BasicMapWithListModel {
2. int code;
3. String result;
4. String message;
5. List data;
6.
7. BasicMapWithListModel.fromJson(Map<String, dynamic> json)
8. : code = json['code'],
9. result = json['result'],
10. message = json['message'],
11. data = (List.from(json['data']))
12. .map((i) => Person.fromJson(i)).toList();
13.
14. @override
15. String toString() {
16. return 'BasicModel: code: $code ,result: $result ,
17. message: $message ,data: ${data.toString()}';
18. }
19.}
这个地方就有点不好理解了,没关系,一步步来,首先,json[‘data’]返回的是一个List<dynamic>,每个元素实际上都是一个Person对象,所以,对于每一个元素,都需要使用Person的fromJson函数来进行转换,借助List的map操作符,就可以非常简单完成这一操作,经过转换之后,就完成了数据的解析工作。