[Flutter] 07-Flutter中反序列化Json

由于Flutter中禁用运行时反射,所以如果项目简单的话可以考虑手动JSON 反序列化,如果你的项目很复杂的话,可以考虑官方建议的 json_serializable 代码生成器库。
在本文中,我们主要讨论对象反序列化,即如何将服务器端返回的字符串数据转化为对象, 其他的Json转Model方式在下章讨论。

  • 首先先要了解什么是序列化和反序列化?

把对象转换为字节序列的过程称为对象的序列化
把字节序列恢复为对象的过程称为对象的反序列化

接下来我们看下几种Json格式,如何反序列化的:

一 简单map

1) 分析的Json数据

分析要诀:

  • 1> Json中用花括号是 Map,用方括号是 List。
  • 2> 对于嵌套结构,首先创建类和构造函数,然后从底层添加工厂方法。

student.json 代码如下:

{
  "id":"123456",
  "name":"codeTao",
  "score" : 99
}

该Json数据很明显是一个Map结构,可以创建一个Student类,通过factory构造方法来反序列化。

2) 与Json结构对应的Model类

student_model.dart 文件代码如下:

class Student{
  String studentId;
  String studentName;
  int studentScores;

  Student({
    this.studentId,
    this.studentName,
    this.studentScores
});

  factory Student.fromJson(Map<String, dynamic> parsedJson){
    return Student(
      studentId: parsedJson['id'],
      studentName : parsedJson['name'],
      studentScores : parsedJson ['score']
    );
  }
}

注意: fromJson 方法中的参数是一个 Map<String, dynamic> ,这意味着直到运行时我们才知道值的类型。

3) 请求数据并反序列化

student_services.dart 中请求数据, 并解析json, 最后调用 Student.fromJson 来反序列化,从 Student 对象中获取值。代码如下:

//1.import 导入相关文件
import 'dart:async' show Future;
import 'package:flutter/services.dart' show rootBundle;
import 'dart:convert';
import 'package:parsing_json_demo/model/student_model.dart'; // 导入模型文件

//2.加载 Json Asset
Future<String> _loadAStudentAsset() async {
  return await rootBundle.loadString('assets/student.json');
}

//3. 加载响应数据
Future loadStudent() async {
  //从 assets 中加载原始 json 字符串
  String jsonString = await _loadAStudentAsset();
  //解析 json 字符串
  final jsonResponse = json.decode(jsonString);
  //通过调用 Student.fromJson 方法反序列化解析的 json,以便可以使用 Student 对象来访问数据
  Student student = new Student.fromJson(jsonResponse);
  //Student 类里打印了 studentScores
  print("student.studentScores= ${student.studentScores}");
}
  • 在这项目中,所有 json 文件放在 assets 文件夹下,所以我们必须这样加载 json。你也可以进行网络调用,网络调用不在这篇文章的讨论范围内。

  • 使用Flutter内置的dart:convert库Json解码器json.decode() 来实现,该方法可以根据 Json 字符串具体内容将其转为 List 或 Map。

注意:请记住上面请求数据并反序列化的 3 个步骤(1.import 导入相关文件; 2.加载 Json Asset; 3.加载响应数据),接下来 json 解析都会用到(只更改文件名和方法名),我不会再重复书写该代码。但你可以在示例项目中查看所有内容。

二. Map含有简单List结构

1) 分析的Json数据

{
  "city": "广州",
  "streets": [
    "北京路",
    "上下九街"
  ]
}
  • 该数据是一个含有 List<String>的Map

2) 与Json结构对应的Model类

class Address {
  final String city;
  final List<String> streets;

  Address({
    this.city,
    this.streets
  });

  factory Address.fromJson(Map<String, dynamic> parsedJson) {
    var streetsFromJson  = parsedJson['streets'];
    print(streetsFromJson.runtimeType); //List<dynamic>
    // 显式地转换成 List<String>
    // List<String> streetsList = new List<String>.from(streetsFromJson);
    List<String> streetsList = streetsFromJson.cast<String>();

    return new Address(
      city: parsedJson['city'],
      streets: streetsList,
    );
  }
}

三 Map含有简单Map嵌套结构

1) 分析的Json数据

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

2) 与Json结构对应的Model类

class Shape{
  String shapeName;
  Property property;

  Shape({
    this.shapeName,
    this.property
  });

  factory Shape.fromJson(Map<String, dynamic> parsedJson){
    return Shape(
      shapeName: parsedJson['shape_name'],
      property: Property.fromJson(parsedJson['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']
    );
  }
}

四 Map含有对象列表的嵌套结构

1) 分析的Json数据

{
  "id":1,
  "name":"盒装牛奶",
  "images":[
    {
      "id":11,
      "imageName":"telunsu.png"
    },
    {
      "id":22,
      "imageName":"wahaha.png"
    }
  ]
}
  • Map含有 对象列表 的嵌套结构

2) 与Json结构对应的Model类

class Product {
  final int id;
  final String name;
  final List<Image> images;

  Product({this.id, this.name, this.images});

  factory Product.fromJson(Map<String, dynamic> parsedJson){
    var list = parsedJson['images'] as List;
    print(list.runtimeType); //List<dynamic>
    List<Image> imagesList = list.map((i) => Image.fromJson(i)).toList();
    //print(imagesList.runtimeType); // map操作后imagesList类型为 'MappedListIterable<dynamic, Image>
    //不转化为List<Image>就会报错: Unhandled Exception: type 'MappedListIterable<dynamic, Image>' is not a subtype of type 'List<Image>'

    return Product(
      id: parsedJson['id'],
      name: parsedJson['name'],
      images: imagesList
    );
  }
}

class Image {
  final int imageId;
  final String imageName;

  Image({this.imageId, this.imageName});

  factory Image.fromJson(Map<String, dynamic> parsedJson){
   return Image(
     imageId:parsedJson['id'],
     imageName:parsedJson['imageName']
   );
  }
}

其中 List<Image> imagesList = list.map((i) => Image.fromJson(i)).toList();
list 在这里是一个 List。现在我们通过调用 Image.fromJson 遍历整个列表,并把 list 中的每个对象映射到 Image 中,然后我们将每个 map 对象放入一个带有 toList() 的新列表中,并将它存储在 List<Image> imagesList。

五 map列表

1) 分析的Json数据

[
  {
    "albumId": 1,
    "id": 1,
    "title": "美丽的小径",
    "url": "http://b.zol-img.com.cn/sjbizhi/images/11/640x1136/1592364145769.jpg",
    "thumbnailUrl": "http://b.zol-img.com.cn/sjbizhi/images/11/480x800/1592364145769.jpg"
  },
  {
    "albumId": 1,
    "id": 2,
    "title": "小朋友说,别笑,我在办公呢?",
    "url": "http://b.zol-img.com.cn/sjbizhi/images/11/640x1136/1592366115586.jpg",
    "thumbnailUrl": "http://b.zol-img.com.cn/sjbizhi/images/11/480x800/1592366115586.jpg"
  },
  {
    "albumId": 1,
    "id": 3,
    "title": "豪华跑车",
    "url": "http://sjbz.fd.zol-img.com.cn/t_s640x1136c/g3/M08/0E/0B/ChMlWF7oPRuIcWdWABs0s8RzN5wAAU0PwJXf0sAGzTL604.jpg",
    "thumbnailUrl": "http://sjbz.fd.zol-img.com.cn/t_s480x800c/g3/M08/0E/0B/ChMlWF7oPRuIcWdWABs0s8RzN5wAAU0PwJXf0sAGzTL604.jpg"
  }
]

2) 与Json结构对应的Model类

class PhotosList {
  final List<Photo> photos;

  PhotosList({
    this.photos,
});

  factory PhotosList.fromJson(List<dynamic> parsedJson) {

    List<Photo> photos = new List<Photo>();
    photos = parsedJson.map((i)=>Photo.fromJson(i)).toList();

    return new PhotosList(
      photos: photos
    );
  }
}

class Photo{
  final String id;
  final String title;
  final String url;

  Photo({
    this.id,
    this.url,
    this.title
}) ;

  factory Photo.fromJson(Map<String, dynamic> json){
    return new Photo(
      id: json['id'].toString(),
      title: json['title'],
      url: json['json'],
    );
  }
}

六 复杂的嵌套结构

1) 分析的Json数据

{
  "page": 1,
  "per_page": 3,
  "total": 12,
  "total_pages": 4,
  "author":{
    "name": "CodeTao"
  },
  "data": [
    {
      "id": 1,
      "name": "Harry",
      "avatar": "https://upload-images.jianshu.io/upload_images/126164-be76091e050f0605.png",
      "images": [
        {
          "id" : 22,
          "imageName": "aaa.jpeg"
        },
        {
          "id" : 23,
          "imageName": "bbb.jpeg"
        }
      ]
    },
    {
      "id": 2,
      "name": "Jame",
      "avatar": "https://upload-images.jianshu.io/upload_images/126164-be76091e050f0605.png",
      "images": [
        {
          "id" : 33,
          "imageName": "ccc.jpeg"
        },
        {
          "id" : 34,
          "imageName": "ddd.jpeg"
        }
      ]
    },
    {
      "id": 3,
      "name": "Henry",
      "avatar": "https://upload-images.jianshu.io/upload_images/126164-be76091e050f0605.png",
      "images": [
        {
          "id" : 44,
          "imageName": "eee.jpeg"
        },
        {
          "id" : 45,
          "imageName": "fff.jpeg"
        }
      ]
    }
  ]
}

2) 与Json结构对应的Model类

class Page{
  int page;
  int perPage;
  int total;
  int totalPages;
  Author author;
  List<Data> data;

  Page({
    this.page,
    this.perPage,
    this.total,
    this.totalPages, this.author, this.data});

  factory Page.fromJson(Map<String, dynamic> parsedJson){

    var list = parsedJson['data'] as List;
    List<Data> data = list.map((i) => Data.fromJson(i)).toList();

    return Page(
        page: parsedJson['page'],
        perPage: parsedJson['per_page'],
        total: parsedJson['total'],
        totalPages: parsedJson['total_pages'],
        author: Author.fromJson(parsedJson['author']),
        data: data
    );
  }
}


class Author{
  String name;

  Author({this.name});

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

class Data{
  int id;
  String name; // add others
  List<Image> imagesList;

  Data({
    this.id, this.name, this.imagesList
});

  factory Data.fromJson(Map<String, dynamic> parsedJson){

    var list = parsedJson['images'] as List;
    List<Image> images = list.map((i) => Image.fromJson(i)).toList();

    return Data(
        id: parsedJson['id'],
        name: parsedJson['name'],
        imagesList: images
    );
  }
}

class Image{
  int id;
  String imageName;

  Image({
  this.id, this.imageName

  });

  factory Image.fromJson(Map<String, dynamic> parsedJson){
    return Image(
        id: parsedJson['id'],
        imageName : parsedJson['imageName'],
    );
  }

}

七 手动Json转Model优缺点:

  • 优点:完全是自己可控的,并且需要哪些字段就转化哪些字段,对于不需要的,忽略即可;并且继承关系也会一目了然
  • 缺点:数据嵌套层级多,会非常麻烦,并且容易出错。

所以如果数据嵌套层级多的话,建议使用dart官方推荐json_serializable方式进行Json转Model ,进一步了解查看下一篇[Flutter] 08-Flutter中的Json转Model

由于笔者水平有限,文中如果有错误的地方,或者有更好的方法,还望大神指出。
附上本文的所有 demo 下载链接,【GitHub】
如果你看完后觉得对你有所帮助,还望在 GitHub 上点个 star。赠人玫瑰,手有余香。

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