Flutter开发之JSON及序列化(29)

本文介绍了使用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>,这意味着我们直到运行时才知道值的类型。 通过这种方法,我们失去了大部分静态类型语言特性:类型安全、自动补全和最重要的编译时异常。这样一来,我们的代码可能会变得非常容易出错。

例如,当我们访问nameemail字段时,我们输入的很快,导致字段名打错了。但由于这个JSON在map结构中,所以编译时不会报错。

2.在模型类中序列化JSON

我们可以通过引入一个简单的模型类(model class)来解决前面提到的问题,我们称之为User。在User类内部,我们有:

一个User.fromJson 构造函数, 用于从一个map构造出一个 User实例 map structure
一个toJson 方法, 将 User 实例转化为一个map.

这样,调用代码现在可以具有类型安全、自动补全字段(nameemail)以及编译时异常。如果我们将拼写错误或字段视为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类。

方式一:在线生成

  1. 首先打开 JSON to Dart 一键生成Model类工具。页面如下:
    在这里插入图片描述
  2. 将json数据赋值到输入框中,点击创建Dart类,然后右边就是生成好的Dart代码,类名可以复制到编辑器后自行修改
    在这里插入图片描述
  3. 创建一个Dart类:ExampleJson.dart,将工具生成的代码copy到这个类中,如下图
    在这里插入图片描述
  4. 解析请求返回的数据,使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:转换代码如下:
  1. 准备好要序列化的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);
}
  1. 第一次创建类时,您会看到与下图类似的错误
    在这里插入图片描述
    这些错误是完全正常的,这是因为model类的生成代码还不存在。为了解决这个问题,我们必须运行代码生成器来为我们生成序列化模板。

  2. 运行代码生成器
    有两种运行代码生成器的方法:

  • 一次性生成
    通过在我们的项目根目录下运行flutter packages pub run build_runner build,我们可以在需要时为我们的model生成json序列化代码。 这触发了一次性构建,它通过我们的源文件,挑选相关的并为它们生成必要的序列化代码。
    虽然这非常方便,但如果我们不需要每次在model类中进行更改时都要手动运行构建命令的话会更好。

  • 持续生成
    使用_watcher_可以使我们的源代码生成的过程更加方便。它会监视我们项目中文件的变化,并在需要时自动构建必要的文件。我们可以通过flutter packages pub run build_runner watch 在项目根目录下运行来启动_watcher_。
    只需启动一次观察器,然后并让它在后台运行,这是安全的。

运行代码生成器后错误警告消失。

  1. 使用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. 解析打印日志。完成了
    在这里插入图片描述

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

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值