本文介绍了使用JSON的两个常规策略:
手动序列化和反序列化
通过代码生成自动序列化和反序列化
不同的项目具有不同的复杂度和场景。对于较小项目,使用代码生成器可能会过度。对于具有多个JSON model
的复杂应用程序,手动序列化可能会比较重复,很容易出错。
小项目手动序列化
手动JSON序列化是指使使用dart:convert
中内置的JSON解码器。它将原始JSON字符串传递给JSON.decode()
方法,然后在返回的Map<String, dynamic>
中查找所需的值。 它没有外部依赖或其它的设置,对于小项目很方便。
使用 dart:convert手动序列化JSON。
Flutter中JSON
序列化非常简单。Flutter有一个内置dart:convert
库,其中包含一个简单的JSON
编码器和解码器。以下是一个简单的user model
的示例JSON。
{
"name": "John Smith",
"email": "john@example.com"
}
有了dart:convert,我们可以用两种方式来序列化这个JSON
。我们来看看这两种方法:
1.内连序列化JSON
通过调用JSON.decode方法来解码JSON ,使用JSON字符串作为参数。
Map<String, dynamic> user = JSON.decode(json);
print('Howdy, ${user['name']}!');
print('We sent the verification link to ${user['email']}.');
JSON.decode()
仅返回一个Map<String, dynamic>
,这意味着我们直到运行时才知道值的类型。 通过这种方法,我们失去了大部分静态类型语言特性:类型安全、自动补全和最重要的编译时异常。这样一来,我们的代码可能会变得非常容易出错。
例如,当我们访问name
或email
字段时,我们输入的很快,导致字段名打错了。但由于这个JSON
在map结构中,所以编译时不会报错。
2.在模型类中序列化JSON
我们可以通过引入一个简单的模型类(model class)来解决前面提到的问题,我们称之为User。在User类内部,我们有:
一个User.fromJson 构造函数, 用于从一个map构造出一个 User实例 map structure
一个toJson 方法, 将 User 实例转化为一个map.
这样,调用代码现在可以具有类型安全、自动补全字段(name
和email
)以及编译时异常。如果我们将拼写错误或字段视为int
类型而不是String
, 那么我们的应用程序就不会通过编译,而不是在运行时崩溃。
user.dart
class User {
final String name;
final String email;
User(this.name, this.email);
User.fromJson(Map<String, dynamic> json)
: name = json['name'],
email = json['email'];
Map<String, dynamic> toJson() =>
{
'name': name,
'email': email,
};
}
现在,序列化逻辑移到了模型本身内部。采用这种新方法,我们可以非常容易地反序列化user。
Map userMap = JSON.decode(json);
var user = new User.fromJson(userMap);
print('Howdy, ${user.name}!');
print('We sent the verification link to ${user.email}.');
要序列化一个user,只是将该User对象传递给该JSON.encode
方法。不需要手动调用toJson这个方法,因为JSON.encode
已经为我们做了。
String json = JSON.encode(user);
这样,调用代码就不用担心JSON序列化了。
在大中型项目中使用代码生成
1. Flutter Json转实体类(一键生成)
http://www.devio.org/io/flutter_app/json/home_page.json
在开发过程中,我们一般都是使用插件或工具一键生成实体类的,这样极大的提高了开发效率,目前我们可以通过在线生成和安装插件生成的方式来一键生成Dart类。
方式一:在线生成
- 首先打开 JSON to Dart 一键生成Model类工具。页面如下:
- 将json数据赋值到输入框中,点击创建Dart类,然后右边就是生成好的Dart代码,类名可以复制到编辑器后自行修改
- 创建一个Dart类:
ExampleJson.dart
,将工具生成的代码copy到这个类中,如下图
- 解析请求返回的数据,使Json 转换为model:转换代码如下:
import 'package:flutter/material.dart';
import 'dart:convert';
import 'dart:io';
import 'package:hello/Models/ExampleJson.dart';
class JsonToModelTest extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return JsonToModelTestView();
}
}
class JsonToModelTestView extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return JsonToModelTestViewState();
}
}
class JsonToModelTestViewState extends State <JsonToModelTestView> {
void _getJsonDataRequest()async {
print('_getJsonDataRequest!');
var url = 'http://www.devio.org/io/flutter_app/json/home_page.json';
var httpClient = new HttpClient();
String result;
try {
var request = await httpClient.getUrl(Uri.parse(url));
var response = await request.close();
print("statusCode----${response.statusCode}");
if (response.statusCode == HttpStatus.ok) {
var responseBody = await response.transform(utf8.decoder).join();
var json = responseBody;
var data = jsonDecode(json);
// print(data.toString());
print("data----$data");
/*先将字符串转成json*/
Map<String, dynamic> mapjson = jsonDecode(responseBody);
/*将Json转成实体类*/
ExampleJson newsBean = ExampleJson.fromJson(mapjson);
/*取值*/
String sats = newsBean.config.searchUrl;
print("sats----$sats");
/*取值*/
String gridNav = newsBean.gridNav.hotel.item1.title;
print("gridNav----$gridNav");
result = 'HttpStatus.ok';
} else {
result = 'Error getting IP address:\nHttp status ${response.statusCode}';
}
} catch (exception) {
result = 'Failed getting IP address';
}
print("result----$result");
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('Json转model测试'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('获取Json数据方法一'),
onPressed: _getJsonDataRequest,
),
],
),
),
);
}
}
打印ExampleJson
的属性数据日志:
方式二:安装FlutterJsonBeanFactory插件生成
首先安装FlutterJsonBeanFactory这个插件,安装方式很简单,这里我就不说了。
安装完成后右键包目录,选择new
然后选择dart bean class File from JSON
然后将json数据粘贴至输入框,输入类名,点击make即可
这样实体类就生成好了,用插件生成也是很方便的。
方式三:dio请求库和json_serializable
序列化数据
代码生成JSON序列化:是指通过外部库为您自动生成序列化模板。它需要一些初始设置,并运行一个文件观察器,从您的
model
类生成代码。
这种方法适用于较大的项目。不需要手写,如果访问JSON字段时拼写错误,这会在编译时捕获的。代码生成的不利之处在于它涉及到一些初始设置。另外,生成的源文件可能会在项目导航器会显得混乱。当一个中、大型项目时,您可能想要使用工具生成JSON序列化。
在项目中pubspec.yaml
添加依赖:json_serializable
json_serializable使用官方文档:https://pub.dev/packages/json_serializable
dependencies:
# Your other regular dependencies here
build_runner: #last
json_serializable: ^3.1.0
运行 flutter packages get
(或者在编辑器中点击 “Packages Get”) 来获取这些新的依赖项.
解析请求返回的数据,使Json 转换为model:转换代码如下:
- 准备好要序列化的Bean类
import 'package:json_annotation/json_annotation.dart';
part 'ImageBean.g.dart';
@JsonSerializable()
class ImageBean{
ImageBean(this.url);
String url;
factory ImageBean.fromJson(Map<String, dynamic> json) => _$ImageBeanFromJson(json);
Map<String, dynamic> toJson() => _$ImageBeanToJson(this);
}
@JsonSerializable()
class ImageListBean{
ImageListBean(this.results,this.error);
List<ImageBean> results;
bool error;
factory ImageListBean.fromJson(Map<String, dynamic> json) => _$ImageListBeanFromJson(json);
Map<String, dynamic> toJson() => _$ImageListBeanToJson(this);
}
-
第一次创建类时,您会看到与下图类似的错误
这些错误是完全正常的,这是因为model类的生成代码还不存在。为了解决这个问题,我们必须运行代码生成器来为我们生成序列化模板。 -
运行代码生成器
有两种运行代码生成器的方法:
-
一次性生成
通过在我们的项目根目录下运行flutter packages pub run build_runner build
,我们可以在需要时为我们的model生成json序列化代码。 这触发了一次性构建,它通过我们的源文件,挑选相关的并为它们生成必要的序列化代码。
虽然这非常方便,但如果我们不需要每次在model类中进行更改时都要手动运行构建命令的话会更好。 -
持续生成
使用_watcher_可以使我们的源代码生成的过程更加方便。它会监视我们项目中文件的变化,并在需要时自动构建必要的文件。我们可以通过flutter packages pub run build_runner watch
在项目根目录下运行来启动_watcher_。
只需启动一次观察器,然后并让它在后台运行,这是安全的。
运行代码生成器后错误警告消失。
- 使用dio库发送网络请求,并完成序列化
import 'package:flutter/material.dart';
import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:hello/Models/ImageBean.dart';
class JsonToModelTest2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return JsonToModelTestView2();
}
}
class JsonToModelTestView2 extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return JsonToModelTestView2State();
}
}
class JsonToModelTestView2State extends State <JsonToModelTestView2> {
void _getJsonDataRequest2222() async {
print("------------------------开始请求--------------------------------------");
Response response = await Dio().get("https://gank.io/api/data/福利/10/1");
var data = response.data;
print(data);
print(response.data is String);
ImageListBean image = ImageListBean.fromJson(data);
/*取值*/
String gridNav = image.results[0].url;
print("gridNav----$gridNav");
print("------------------------请求结束---------------------------------------");
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('Json转model测试'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('Dio获取Json数据'),
onPressed: _getJsonDataRequest2222,
),
],
),
),
);
}
}
- 解析打印日志。完成了
在1.准备好要序列化的Bean类
步骤中。我们可以使用在线 json_serializable工具来生成Bean类。如下图:
将右边生成的实体类ImageJson
copy到一个Dart文件中。并修改一致名字。如果有this.Id
报错请改成this.id
然后终端运行:
flutter packages pub run build_runner build
flutter packages pub run build_runner watch
就完成了实体类的序列化工作了。
void _getJsonDataRequest2222() async {
print("------------------------开始请求2222--------------------------------------");
Response response = await Dio().get("https://gank.io/api/data/福利/10/1");
var data = response.data;
print(data);
print(response.data is String);
ImageJson imageModel = ImageJson.fromJson(data);
/*取值*/
String gridNav = imageModel.results[0].url;
print("gridNav----$gridNav");
print("------------------------请求结束2222---------------------------------------");
}
参考文章:
6.1-Flutter中Json解析和模型转换
Flutter json转实体类(插件自动生成)
Flutter 联网和JSON转换成Bean