Flutter学习小计:实现json数据的解析

前言
最近Google开源的跨平台移动开发框架Flutter非常火热,推出了1.0的正式版,趁着热度,我也是抽空粗略地学习了一下。目前网上Flutter相关的资料和开源项目也非常多了,在学习的过程中给了我很多帮助。因此,我想通过一系列文章记录一下自己学习Flutter遇到的一些问题,既是对自身技术的巩固,也方便日后即时查阅。

本文介绍一下Flutter中如何进行json数据的解析。在移动端开发中,请求服务端返回json数据并解析是一个很常见的使用场景。Android原生开发中,有GsonFormat这样的神器,一键生成JavaBean,并利用Gson实现json数据和对象的转化;在React Native中更是得益于直接使用javascript语言,无需对json对象进行解析便可以直接访问属性。那么在Flutter中如何实现对json数据的解析呢?
Flutter采用dart语言进行开发,dart具有很多核心库,其中dart:convert库中内置了json转换器,可以实现将json数据转换成dart对象。简单的使用如下:

import 'dart:convert';

void main() {
  // 解析对象
  String jsonStr1 = '{"name":"Curry","email":"SC@GSW.com"}';
  Map<String, dynamic> map = json.decode(jsonStr1);
  print(map['name']);

  // 解析列表
  String jsonStr2 = '[{"name":"Curry"},{"name":"Thompson"}]';
  List list = json.decode(jsonStr2);
  // 输出列表第一个对象的"name"属性
  print(list[0]["name"]);
}

解析json串的方法很简单,直接调用json.decode()即可,但是由于json.decode()方法返回类型为dynamic,因此无法进行类型的检查,编译时不会报错,容易使程序发生错误。采取的做法是像原生开发一样,新建一个model实体类,将json转为实体类对象。生成实体类的方法有以下两种:

方法一.手写实体类

一个简单的实体类实例如下

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.fromJson和toJson,其中,User.fromJson是一个命名构造函数,通过传入的map构造出实体类对象;toJson()方法用于将实体类对象序列化为json字符串。
使用方法如下:

import 'dart:convert';
// 这里需要替换为实体类所在路径
import 'package:json_parse_test/user.dart';

void main() {
  // 解析对象
  String jsonStr1 = '{"name":"Curry","email":"SC@GSW.com"}';
  Map<String, dynamic> map = json.decode(jsonStr1);
  User user = User.fromJson(map);
  print(user.name);

  // 解析列表
  String jsonStr2 = '[{"name":"Curry"},{"name":"Thompson"}]';
  List list= json.decode(jsonStr2);
  // 将列表中的第一个对象转换成User对象
  print(User.fromJson(list[0]).name);
  
  // 将对象序列化为json串
  // json.encode()会自动调用实体类中的toJson()
  String jsonText = json.encode(user);
  print(jsonText); 
}

这样就完成了将json转化成实体对象,步骤很简单,但是还有一个问题是,真的每个实体类都要我们自己去手写吗,有没有类似GsonFormat这种一键生成实体类的插件呢。当然是有的,之前从鸿洋大神推荐的Flutter学习资源中偶然发现了一个神奇的网站:
https://javiercbk.github.io/json_to_dart/

JSON To Dart.png

可以直接通过json生成实体类代码,仿佛是发现了新大陆,不得不佩服开发出这些快捷工具的大神们,为后面学习的人铺平了道路。网站的操作很简单,一看就会。
最近偶然发现Android Studio也有了相关插件,我使用的是FlutterJsonBeanFactory,安装过程我就不说了,装好在目录下右键依次选择New、dart bean class File from JSON,之后会弹出一个窗口,填入json字符串和实体类名称,就可以像GsonFormat那样自动生成dart实体类文件啦。

需要注意的是默认情况生成的文件名和类名会加上entity后缀,可以从设置中进行配置,依次点击File→Settings,从Tools下选择FlutterJsonBeanFactory,修改model suffix即可。

方法二.通过json_serializable自动生成

json_serializable是Google官方推荐的一个json序列化库。使用之前需要在pubspec.yaml文件中添加依赖项:
pubspec.yaml

dependencies:
  # Your other regular dependencies here
  json_annotation: ^2.0.0

dev_dependencies:
  # Your other dev_dependencies here
  build_runner: ^1.0.0
  json_serializable: ^2.0.0

添加之后在项目根目录文件夹中运行flutter packages get (或者在编辑器中点击 “Packages Get”) 以在项目中使用这些新的依赖项。
使用时也是需要新建一个实体类

import 'package:json_annotation/json_annotation.dart';

// user.g.dart通过命令自动生成
part 'user_model.g.dart';

@JsonSerializable()
class User {
  // 自定义json字段名对应的属性名,不是必须的
  @JsonKey(name: 'userName')
  String name;
  String email;

  User(this.name, this.email);

  factory User.fromJson(Map<String, dynamic> json) =>
      _$UserFromJson(json);

  Map<String, dynamic> toJson() => _$UserToJson(this);
}

需要我们手写的就这些,这时代码会报错,我们需要运行命令来自动生成缺少的文件,有两种生成的方法:

  • 一次性生成
    在项目根目录文件夹下运行
flutter packages pub run build_runner build
  • 持续生成
    在项目根目录文件夹下运行
flutter packages pub run build_runner watch

两者的区别是,一次性生成只执行一次,后面每新建一个实体类都需要重新运行该命令;持续生成会在我们编写代码的过程中根据需要自动生成缺少的文件。
之后解析json的方法和方法一相同

import 'dart:convert';
// 这里需要替换为实体类所在路径
import 'package:json_parse_test/user.dart';

void main() {
  // 解析对象
  String jsonStr = '{"name":"Curry","email":"SC@GSW.com"}';
  Map<String, dynamic> map = json.decode(jsonStr);
  User user = User.fromJson(map);
  print(user.name);
}

这里关于实体类的生成我找到了做人要简单这位大神写的网页工具,同样是可以很方便地将json转换成实体类代码。

json_serializable实体类生成工具

生成代码后点击下载将实体类dart文件下载到本地,之后放到项目中,在项目根目录运行上面提到的生成文件命令即可。
当然也可以通过自已定义模板编写脚本来生成实体类文件,具体做法可以参考《Flutter实战》

总结

Flutter中json的解析分为两步:
1.通过json.decode()将json串转换成dart对象(Map或List)
2.编写实体类(两种方法,手写或自动生成),利用上一步得到的dart对象构造出实体类对象
其实在实际的开发中,有可能并不需要第一步,这和服务端返回给我们的数据格式有关(响应头中的Content-Type),如果服务端直接返回给我们json格式(对应Content-Type:application/json),我们可以直接从返回的响应中获取到dart对象,省去了第一步,直接转化成实体类对象即可;如果服务端返回给我们的是纯文本格式(对应Content-Type:text/html,这里不一定是html,也有可能是xml等其他格式),则需要先将响应的字符串转换成dart对象。

参考资料

《Flutter实战》Json转Model
flutter json解析相关 for json_serializable

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容