前言
前一篇博文已经详细介绍了Flutter开发中的网络请求,但其实大多数项目中,返回HTML内容是不够的,因为移动端使用的最多的请求是JSON数据,所以我们需要掌握Flutter开发中,JSON解析的知识。JSON(javaScript Object Notation,JS对象简谱)是一种轻量级的数据交换格式)
JSON转换成Dart对象
假设,我们现在是开发的是一款新闻App,通过访问相关的接口之后,服务器返回了这样一条简单的JSON数据,如下图所示:
{"title":"疫情疫苗出世,多板块重大利好"}
那么我们应该如何处理这条数据显示在界面上呢?相信有过Java开发Android经验的读者,肯定知道如何把这段数据还原成一个对象,并且在界面显示出来。同样,在Flutter开发中,也可以把这个JSON数据转换为Dart对象,我们先定义Dart对象News,代码如下:
class News{ final String title; News({this.title}); factory News.fromJson(Map<String,dynamic> json){ return News( title: json['title'], ); } }
在dart:convert里面有一个JSON常量,它是负责处理服务端返回的JSON数据的,在请求响应回来的时,通过json.decode(response.body)方法调用可以把JSON结果转换城Map类型或List类型。如果是一个JSON对象,返回将是一个Map;如果是JSON数组,则会返回List。
上面代码之所以把map的value定义成dynamic,是因为不肯定value的类型,毕竟有可能是字符串,有可能是整型,还是用这个自动匹配类型最实在。
实践
在JSON解析之后,我们需要把结果通过界面的形式展现给用户看,所以下面我们直接来实现其功能:
class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(news: httpPost()); } class _MyHomePageState extends State<MyHomePage> { final Future<News> news; _MyHomePageState({Key key,this.news}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("HttpClient"), ), body:Center( child: FutureBuilder<News>( future: news, builder: (context,snapshot){ if(snapshot.hasData){ return Text(snapshot.data.title); }else if(snapshot.hasError){ return Text("错误啦"); } return CircularProgressIndicator(); }, ), ), ); } } Future<News> httpPost() async{ final response=await http.get("http://liyuanjinglyj.com/demo/"); if(response.statusCode==200){ print(utf8.decode(response.bodyBytes)); return News.fromJson(json.decode(response.body)); }else{ throw Exception('请求不到JSon'); } } class News{ final String title; News({this.title}); factory News.fromJson(Map<String,dynamic> json){ return News( title: json['title'], ); } }
代码很简单,这里专门定义了一个JSON的Dart类,用于处理JSON数据,同时用FutureBuilder组件将JSON显示在屏幕上。显示效果首图所示。
根据JSON用工具生成实体类
上面可以说是静态生成JSON格式,也就是说手动定义了一个JSON类,但是假如有许多许多JSON格式的数据,那怎么获取JSON数据呢?一个一个写肯定麻烦的很,所以我们需要借助工具去自动生成JSON类,这里就也需要在pubspec.yaml中导入依赖:
dependencies: json_annotation: ^2.0.0 dev_dependencies: build_runner: ^1.0.0 json_serializable: ^2.0.0
然后我们创建一个实体类,格式如下:
import 'package:json_annotation/json_annotation.dart'; part 'user.g.dart'; @JsonSerializable() class User{ String name; String email; User({this.name,this.email}); factory User.fromJson(Map<String,dynamic> json)=>_$UserFromJson(json); Map<String,dynamic> toJson=>_$UserToJson(this); }
这里会报错,特别是part ‘user.g.dart’;与最后两句,都会出现红色的波浪线提示,但这是正常的不用急,我们可以把上面的代码看成是模板,然后我们在工程目录文件下输入如下命令:
flutter packages pub run build_runner build
输入完这个命令之后,就会生成一个user.g.dart文件,在user.dart同级目录之中,代码如下:
// GENERATED CODE - DO NOT MODIFY BY HAND part of 'user.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** User _$UserFromJson(Map<String, dynamic> json) { return User(name: json['name'] as String, email: json['email'] as String); } Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{'name': instance.name, 'email': instance.email};
博主为了直观,所以减少了许多参数,但在实际情况中,JSON的参数肯定会有很多,甚至还有层级,那么这种情况下,这样自动生成起来肯定比较方便,也比较节约时间。
但这种方式也会有一个缺陷,就是假如我的JSON格式变更了,那还不得一次一次生成?很显然,Flutter也考虑到这种情况,所以提供给我们一种监听模式来实现每一次的生成,命令如下:
flutter packages pub run build_runner watch
这个命令只需要执行一次,那么就会一直监听在后台运行,在也不要我们操作什么就可以自动生成了。
当JSON类属性与服务器返回属性不一致时
虽然说上面的操作很方便,但自动生成还有一个待解决的问题,那么就是当自己的JSON类与服务器端返回的字段不一致的时候怎么办?当然你可以直接更改字段,但可能一改就要动全部的内容,所以这个时候使用JsonKey注解的方式就比较高效了,比如:
@JsonKey(name:'user_name') final String userName;
这样注解加自动生成JSON类就非常完美高效,可以大大提高程序的开发效率。