[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 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。赠人玫瑰,手有余香。