Flutter Json渐进式解析(上)

Flutter修仙之旅,进度有点慢哈,毕竟修仙之余还得上班,今天给大家介绍Flutter中的Json幻化技巧,助大家修仙一臂之力

Json解析是平时日常开发的一个非常重要的部分,大部分从接口返回的数据都是Json格式,客户端通过解析Json数据来进行UI界面的绘制和展示。

Flutter给开发者提供了一个非常方便的解析库—— dart:convert来帮助开发者进行Json解析的相关操作。

下面,通过梳理平时开发中常用的一些Json数据格式,来一起看下如何使用dart:convert库来进行Json解析。

dart:convert

首先,来了解下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的序列化。

JsonObject格式

这个应该是最简单的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解析基本都是一致的。

JsonObject格式_带有数组格式数据

第一种格式中,都是基本数据类型,下面再增加一个数组类型的数据,如下所示,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类型,这样解析就没有问题了。

JsonObject格式_嵌套JsonObject数据

前面的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']);  

通过类型转换,就完成了解析。

JsonObject格式_带有List类型JsonObject数据

在前一种数据格式的基础上,再进行进一步的嵌套,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操作符,就可以非常简单完成这一操作,经过转换之后,就完成了数据的解析工作。

原文发布于微信公众号 - Android群英传(android_heroes)

原文发表时间:2019-04-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券