Flutter 中的 JSON 解析

JSON 是我们开发中最常使用的一种数据格式,这篇文章中,我们主要看看在开发中最常见的几种格式的 JSON 数据在 Flutter 中的解析:

以下案例中,我们都会将json文件放到本地,也就是 assets 文件中,然后从本地读取这些文件进行解析。

如我们需要读取 assets/person.json :

image

那么就需要在 pubspec.yaml 中做如下配置:

flutter:
    uses-material-design:  true
    # 资源文件配置
    assets:
        -  assets/person.json

下面我们就来解析一些常见的 Json 格式的数据。

如果对 Dart 基础还不是很了解,可以先看看这篇文章 Dart 基础入门

简单的对象解析

定义一个 person.json 如下:

{
    "name":  "jack",
    "age":  18,
    "height":  175.0
}

和在 Java 里面一样,我们首先需要构建一个 Person 的实体类,如下:

class Person {
  String name;
  int age;
  double height;

  Person({this.name, this.age, this.height});

  factory Person.fromJson(Map<String, dynamic> json) {
    return Person(name: json['name'], age: json['age'], height: json['height']);
  }
}

接着,我们在创建一个 person_service.dart 来解析 person.json。

该类中需要导入如下几个依赖库:

// 使用该库中的 rootBundle 对象来读取 perosn.json 文件
import 'package:flutter/services.dart';  
// json
import 'dart:convert';  
// 异步 Future
import 'dart:async';

person_service.dart

import 'package:flutter/services.dart';
import 'dart:convert';
import 'dart:async';
import '../models/person.dart';

// 读取 assets 文件夹中的 person.json 文件
Future<String> _loadPersonJson() async {
  return await rootBundle.loadString('assets/person.json');
}

// 将 json 字符串解析为 Person 对象
Future<Person> decodePerson() async {
  // 获取本地的 json 字符串
  String personJson = await _loadPersonJson();

  // 解析 json 字符串,返回的是 Map<String, dynamic> 类型
  final jsonMap = json.decode(personJson);

  print('jsonMap runType is ${jsonMap.runtimeType}');

  Person person = Person.fromJson(jsonMap);

  print(
      'person name is ${person.name}, age is ${person.age}, height is ${person.height}');

  return person;
}

输入如下:

flutter: jsonMap runType is _InternalLinkedHashMap<String, dynamic>
flutter: person name is jack, age is 18, height is 175.0

可以看出 json.decode(personJson) 方法返回的类型为 _InternalLinkedHashMap<String, dynamic> ,意思就是这个 Map 的 key 为 String 类型,而 value 的类型为 dynamic 的,也就是动态的,就如 person.json 中,key 都是 String 类型的,但是 value 可能是 String 、int、double 等等类型。

包含数组的对象

定义一个 country.json 如下:

{
    "name": "China",
    "cities": [
        "Beijing",
        "Shanghai"
    ]
}

实体类如下:

class Country {  
  String name;  
  List<String> cities;  
  
  Country({this.name, this.cities});  
  
  factory Country.fromJson(Map<String, dynamic> json) {  
  return Country(name: json['name'], cities: json['cities']);  
  }  
}

Service 类如下:

import 'dart:async';
import 'package:flutter/services.dart';
import 'dart:convert';
import '../models/country.dart';

Future<String> _loadCountryJson() async {
  return await rootBundle.loadString('assets/country.json');
}

Future<Country> decodeCountry() async {
  String countryJson = await _loadCountryJson();

  Map<String, dynamic> jsonMap = json.decode(countryJson);

  Country country = Country.fromJson(jsonMap);
  print('country name is ${country.name}');
  return country;
}

然后我们在 main() 中去调用 decodeCountry() 运行,报错了...

Unhandled Exception: type 'List<dynamic>' is not a subtype of type 'List<String>'
...

错误日志说 List<dynamic> 不是 List<String> 的子类型,也就是我们在country的实体类中直接给 cities 属性赋值为 cities: json['cities'],我们先来看看 json['cities'] 是什么类型:

factory Country.fromJson(Map<String, dynamic> json) {  
  print('json["cities"] type is ${json['cities'].runtimeType}');  
  return Country(name: json['name'], cities: json['cities']);  
}

输出如下:

flutter: json["cities"] type is List<dynamic>

这个时候我们需要将 Country.fromJson(...) 方法作如下更改:

factory Country.fromJson(Map<String, dynamic> json) {
    print('json["cities"] type is ${json['cities'].runtimeType}');
    var originList = json['cities'];
    List<String> cityList = new List<String>.from(originList);
    return Country(name: json['name'], cities: cityList);
  }

上述代码中,我们创建了一个 List<String> 类型的数组,然后将 List<dynamic> 数组中的元素都添加到了 List<String> 中。输出如下:

flutter: json["cities"] type is List<dynamic>
flutter: country name is China

对象嵌套

定义一个 shape.json ,格式如下:

{  
  "name": "rectangle",  
  "property": {  
  "width": 5.0,  
  "height": 10.0  
  }  
}

实体如下:

class Shape {  
  String name;  
  Property property;  
  
  Shape({this.name, this.property});  
  
  factory Shape.fromJson(Map<String, dynamic> json) {  
  return Shape(name: json['name'], property: json['property']);  
  }  
}  
  
class Property {  
  double width;  
  double height;  
  
  Property({this.width, this.height});  
  
  factory Property.fromJson(Map<String, dynamic> json) {  
  return Property(width: json['width'], height: json['height']);  
  }  
}

Service 类如下:

import 'dart:async';  
import 'dart:convert';  
import 'package:flutter/services.dart';  
import '../models/shape.dart';  
  
Future<String> _loadShapeJson() async {  
  return await rootBundle.loadString('assets/shape.json');  
}  
  
Future<Shape> decodeShape() async {  
  String shapeJson = await _loadShapeJson();  
  
  Map<String, dynamic> jsonMap = json.decode(shapeJson);  
  
  Shape shape = Shape.fromJson(jsonMap);  
  
  print('shape name is ${shape.name}');  
  return shape;  
}

运行之后,会报如下错误:

Unhandled Exception: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Property'

也就是说 property: json['property'] 这里赋值的类型是 _InternalLinkedHashMap<String, dynamic> 而不是 Propertyjson['property'] 的值是这样的 {width: 5.0, height: 10.0},它是一个 Map ,并不是一个 Property 对象,我们需要先将这个 Map 转化为对象,然后在赋值:

factory Shape.fromJson(Map<String, dynamic> json) {  
  print('json["property"] is ${json['property']}');  
  Property property = Property.fromJson(json['property']);   // new line
  return Shape(name: json['name'], property: property);  
}

输出:

shape name is rectangle

复杂的对象数组嵌套

{
  "id": "0302",
  "class_name": "三年二班",
  "students": [
    {
      "name": "叶湘伦",
      "sex": "男"
    },
    {
      "name": "路小雨",
      "sex": "女"
    }
  ]
}

实体:

class ClassInfo {
  String id;
  String name;
  List<Student> studentList;

  ClassInfo({this.id, this.name, this.studentList});

  factory ClassInfo.fromJson(Map<String, dynamic> json) {
    return ClassInfo(
        id: json['id'],
        name: json['class_name'],
        studentList: json['students']);
  }
}

class Student {
  String name;
  String sex;

  Student({this.name, this.sex});

  factory Student.fromJson(Map<String, dynamic> json) {
    return Student(name: json['name'], sex: json['sex']);
  }
}

service:

import 'dart:async';
import 'dart:convert';
import 'package:flutter/services.dart';
import '../models/class_info.dart';

Future<String> _loadClassInfoJson() async {
  return await rootBundle.loadString('assets/class_info.json');
}

Future<ClassInfo> decodeClassInfo() async {
  String classInfoJson = await _loadClassInfoJson();

  Map<String, dynamic> jsonMap = json.decode(classInfoJson);

  ClassInfo classInfo = ClassInfo.fromJson(jsonMap);
  classInfo.studentList
      .forEach((student) => print('student name is ${student.name}'));
  return classInfo;
}

上述代码在运行后还是会报错:

Unhandled Exception: type 'List<dynamic>' is not a subtype of type 'List<Student>'

同样,还是在 studentList: json['students'] 出问题了,我们把 json['students'] 的输出来看看:

[{name: 叶湘伦, sex: 男}, {name: 路小雨, sex: 女}]

上述结果的类型为 List<dynamic> 。现在我们需要将 List<dynamic> 转换到一个 List<Student> 类型的数组中,这里需要用到一个操作符 mapmap 操作符的作用就是将某种类型转换为另一种类型。如下:

factory ClassInfo.fromJson(Map<String, dynamic> json) {  
  final originList = json['students'] as List;  
  List<Student> studentList =  
      originList.map((value) => Student.fromJson(value)).toList();  
  return ClassInfo(  
  id: json['id'], name: json['class_name'], studentList: studentList);  
}

输出:

flutter: student name is 叶湘伦
flutter: student name is 路小雨

单纯的数组

member.json

[
  {
    "id": 1,
    "name": "Jack"
  },
  {
    "id": 2,
    "name": "Rose"
  },
  {
    "id": 3,
    "name": "Karl"
  }
]

实体:

class MemberList {  
  List<Member> memberList;  
  
  MemberList({this.memberList});  
  
  factory MemberList.fromJson(List<dynamic> listJson) {  
  
  List<Member> memberList =  
        listJson.map((value) => Member.fromJson(value)).toList();  
  
  return MemberList(memberList: memberList);  
  }  
}  
  
class Member {  
  int id;  
  String name;  
  
  Member({this.id, this.name});  
  
  factory Member.fromJson(Map<String, dynamic> json) {  
  return Member(id: json['id'], name: json['name']);  
  }  
}

因为 member.json 是一个单纯的数组,所以上述代码中我们创建了一个 MemberList 类来将这个 Member 数组包含起来。

注意下上述代码中 MemberList.fromJson(...) 中的写法。

service:

import 'dart:async';
import 'package:flutter/services.dart';
import 'dart:convert';
import '../models/member.dart';

Future<String> _loadMemberJson() async {
  return await rootBundle.loadString('assets/member.json');
}

Future<MemberList> decodeMemberList() async {
  String memberListJson = await _loadMemberJson();

  List<dynamic> list = json.decode(memberListJson);

  MemberList memberList = MemberList.fromJson(list);

  memberList.memberList
      .forEach((member) => print('member name is ${member.name}'));

  return memberList;
}

输出:

flutter: member name is Jack
flutter: member name is Rose
flutter: member name is Karl

复杂的 Json 解析

之前的文章 Flutter 中 ListView 的使用 中用到了 豆瓣API ,这个 API 中返回的数据包含了当前热播的电影,大家尝试着按需解析一下吧 !!!

image

如有错误,还请指出。谢谢!!!

源码地址

参考链接:

https://medium.com/flutter-community/parsing-complex-json-in-flutter-747c46655f51

https://github.com/flutter/flutter/issues/9318

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

推荐阅读更多精彩内容