Flutter开发(11)- JSON转Model

Flutter如何JSON转Model

    在开发中,服务端通常给我们返回的是JSON数据,我们需要将JSON数据转成我们的模型对象来使用。

    在Flutter中,有几种JSON转模型的方式,我们还是以豆瓣为例,来进行一个演练;

一. 豆瓣数据

这里我们使用豆瓣的请求地址:

    https://douban.uieee.com/v2/movie/top250?start=0&count=20

在浏览器中请求,获取到的数据如下:

注意:这里我使用了一个格式化插件:FeHelper,所以结构看起来很清晰

这个数据还是比较复杂的:

    如果我们希望在Flutter代码中使用,直接将JSON转成Map来使用也可以,但是非常麻烦,而且类型会不容易确定,并且不安全;

    所以对于面向对象开发的语言,我们通常都会将它转成模型对象,之后使用一个个模型对象;

我们一起来探究一下,目前Flutter中比较常见的将JSON转成模型的方式。

二. 手动转化

JSON转模型,必然可以通过手动来进行转化:

优点:完全是自己可控的,并且需要哪些字段就转化哪些字段,对于不需要的,忽略即可;并且继承关系也会一目了然

缺点:麻烦,并且容易出错;

下面是我之前针对上面的数据,写的JSON转Model的模型类:

class Person {

  String name;

  String avatarURL;

  Person.fromMap(Map<String, dynamic> json) {

    this.name = json["name"];

    this.avatarURL = json["avatars"]["medium"];

  }

}

class Actor extends Person {

  Actor.fromMap(Map<String, dynamic> json): super.fromMap(json);

}

class Director extends Person {

  Director.fromMap(Map<String, dynamic> json): super.fromMap(json);

}

int counter = 1;

class MovieItem {

  int rank;

  String imageURL;

  String title;

  String playDate;

  double rating;

  List<String> genres;

  List<Actor> casts;

  Director director;

  String originalTitle;

  MovieItem.fromMap(Map<String, dynamic> json) {

    this.rank = counter++;

    this.imageURL = json["images"]["medium"];

    this.title = json["title"];

    this.playDate = json["year"];

    this.rating = json["rating"]["average"];

    this.genres = json["genres"].cast<String>();

    this.casts = (json["casts"] asList<dynamic>).map((item) {

      return Actor.fromMap(item);

    }).toList();

    this.director = Director.fromMap(json["directors"][0]);

    this.originalTitle = json["original_title"];

  }

}

三. json_serializable

json_serializable是dart官方推荐和提供的JSON转Model的方式:

    一个自动化源代码生成器来为你生成 JSON 序列化数据模板;

    由于序列化数据代码不再需要手动编写或者维护,你可以将序列化 JSON 数据在运行时的异常风险降到最低;

第一步:添加相关的依赖

依赖分为项目依赖(dependencies),开发依赖(dev_dependencies):

    注意:需要执行flutter pub get确保我们的项目中有这些依赖

dependencies:

  json_annotation:^3.0.1

dev_dependencies:

  json_serializable:^3.2.5

  build_runner:^1.8.0

第二步:以json_serializable 的方式创建模型类

    这里不以豆瓣数据为例,以一个简单的Json数据作为例子

final jsonInfo = {

    "nickname": "coderwhy",

    "level": 18,

    "courses": ["语文", "数学", "英语"],

    "register_date": "2222-2-22",

    "computer": {

      "brand": "MackBook",

      "price": 1000

    }

  };

创建对应的模型(以json_serializable 的方式,创建完成后代码是报错的)

1.part 'user.g.dart'

    这个是之后json_serializable会自动帮助我们生成的文件

2.JsonSerializable()

    注解:告诉json_serializable哪一个类需要进行转换

3.JsonKey

    当映射关系不一样时,可以指定映射关系

4.另外,这里必须有我们的构造方法

5.需要有对应的工厂构造器

    _UserFromJson(json)和_UserToJson(this)调用的该方法目前会报错,需要json_serializable来生成

6.toString方法不是必须的,是待会儿进行测试的

User类的代码:

import'package:json_annotation/json_annotation.dart';

import'model/computer.dart';

part'user.g.dart';

@JsonSerializable()

class User {

  String name;

  String email;

  @JsonKey(name: "register_date")

  String registerDate;

  List<String> courses;

  Computer computer;

  User(this.name, this.email, this.registerDate, this.courses, this.computer);

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

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

  @override

  String toString() {

    return'User{name: $name, email: $email, registerDate: $registerDate, courses: $courses, computer: $computer}';

  }

}

Computer类的代码:

import'package:json_annotation/json_annotation.dart';

part'computer.g.dart';

@JsonSerializable()

class Computer {

  String brand;

  double price;

  Computer(this.brand, this.price);

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

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

  @override

  String toString() {

    return'Computer{brand: $brand, price: $price}';

  }

}

第三步:生成JSON序列化代码

在项目终端运行下面的指令:

    该指令是生成一次JSON序列化的代码

flutter pub run build_runner build

或运行下面的指令:

    会监听文件的改变,重新生成JSON序列化的代码

flutter pub run build_runner watch

第四步:测试代码

final jsonInfo = {

  "nickname": "coderwhy",

  "level": 18,

  "courses": ["语文", "数学", "英语"],

  "register_date": "2222-2-22",

  "computer": {

    "brand": "MackBook",

    "price": 1000

  }

};

final user = User.fromJson(jsonInfo);

print(user);

更多资料,请查看下面的资源:

    dart:convert 和 JsonCodec 文档

    Pub 中的 json_serializable package

    GitHub 中的 json_serializable 例子

四. 网页转换

目前有一些网页,可以直接将JSON转成Model

    网页推荐:https://javiercbk.github.io/json_to_dart/

我们这里以网页版本为例,非常简单:

    注意:可能因为豆瓣的数据过于复杂,所以在生成的时候发现少了一个Directors类

    这里我重新复制对应的JSON,再次生成了一下

class MovieItem {

  Rating rating;

  List<String> genres;

  String title;

  List<Casts> casts;

  List<String> durations;

  int collectCount;

  String mainlandPubdate;

  bool hasVideo;

  String originalTitle;

  String subtype;

  List<Directors> directors;

  List<String> pubdates;

  String year;

  Avatars images;

  String alt;

  String id;

  MovieItem(

      {this.rating,

        this.genres,

        this.title,

        this.casts,

        this.durations,

        this.collectCount,

        this.mainlandPubdate,

        this.hasVideo,

        this.originalTitle,

        this.subtype,

        this.directors,

        this.pubdates,

        this.year,

        this.images,

        this.alt,

        this.id});

  MovieItem.fromJson(Map<String, dynamic> json) {

    rating =

    json['rating'] != null ? new Rating.fromJson(json['rating']) : null;

    genres = json['genres'].cast<String>();

    title = json['title'];

    if (json['casts'] != null) {

      casts = newList<Casts>();

      json['casts'].forEach((v) {

        casts.add(new Casts.fromJson(v));

      });

    }

    durations = json['durations'].cast<String>();

    collectCount = json['collect_count'];

    mainlandPubdate = json['mainland_pubdate'];

    hasVideo = json['has_video'];

    originalTitle = json['original_title'];

    subtype = json['subtype'];

    if (json['directors'] != null) {

      directors = newList<Directors>();

      json['directors'].forEach((v) {

        directors.add(new Directors.fromJson(v));

      });

    }

    pubdates = json['pubdates'].cast<String>();

    year = json['year'];

    images =

    json['images'] != null ? new Avatars.fromJson(json['images']) : null;

    alt = json['alt'];

    id = json['id'];

  }

  Map<String, dynamic> toJson() {

    finalMap<String, dynamic> data = newMap<String, dynamic>();

    if (this.rating != null) {

      data['rating'] = this.rating.toJson();

    }

    data['genres'] = this.genres;

    data['title'] = this.title;

    if (this.casts != null) {

      data['casts'] = this.casts.map((v) => v.toJson()).toList();

    }

    data['durations'] = this.durations;

    data['collect_count'] = this.collectCount;

    data['mainland_pubdate'] = this.mainlandPubdate;

    data['has_video'] = this.hasVideo;

    data['original_title'] = this.originalTitle;

    data['subtype'] = this.subtype;

    if (this.directors != null) {

      data['directors'] = this.directors.map((v) => v.toJson()).toList();

    }

    data['pubdates'] = this.pubdates;

    data['year'] = this.year;

    if (this.images != null) {

      data['images'] = this.images.toJson();

    }

    data['alt'] = this.alt;

    data['id'] = this.id;

    return data;

  }

}

class Rating {

  int max;

  double average;

  Details details;

  String stars;

  int min;

  Rating({this.max, this.average, this.details, this.stars, this.min});

  Rating.fromJson(Map<String, dynamic> json) {

    max = json['max'];

    average = json['average'];

    details =

    json['details'] != null ? new Details.fromJson(json['details']) : null;

    stars = json['stars'];

    min = json['min'];

  }

  Map<String, dynamic> toJson() {

    finalMap<String, dynamic> data = newMap<String, dynamic>();

    data['max'] = this.max;

    data['average'] = this.average;

    if (this.details != null) {

      data['details'] = this.details.toJson();

    }

    data['stars'] = this.stars;

    data['min'] = this.min;

    return data;

  }

}

class Details {

  int i1;

  int i2;

  int i3;

  int i4;

  int i5;

  Details({this.i1, this.i2, this.i3, this.i4, this.i5});

  Details.fromJson(Map<String, dynamic> json) {

    i1 = json['1'];

    i2 = json['2'];

    i3 = json['3'];

    i4 = json['4'];

    i5 = json['5'];

  }

  Map<String, dynamic> toJson() {

    finalMap<String, dynamic> data = newMap<String, dynamic>();

    data['1'] = this.i1;

    data['2'] = this.i2;

    data['3'] = this.i3;

    data['4'] = this.i4;

    data['5'] = this.i5;

    return data;

  }

}

class Casts {

  Avatars avatars;

  String nameEn;

  String name;

  String alt;

  String id;

  Casts({this.avatars, this.nameEn, this.name, this.alt, this.id});

  Casts.fromJson(Map<String, dynamic> json) {

    avatars =

    json['avatars'] != null ? new Avatars.fromJson(json['avatars']) : null;

    nameEn = json['name_en'];

    name = json['name'];

    alt = json['alt'];

    id = json['id'];

  }

  Map<String, dynamic> toJson() {

    finalMap<String, dynamic> data = newMap<String, dynamic>();

    if (this.avatars != null) {

      data['avatars'] = this.avatars.toJson();

    }

    data['name_en'] = this.nameEn;

    data['name'] = this.name;

    data['alt'] = this.alt;

    data['id'] = this.id;

    return data;

  }

}

class Directors {

  Avatars avatars;

  String nameEn;

  String name;

  String alt;

  String id;

  Directors({this.avatars, this.nameEn, this.name, this.alt, this.id});

  Directors.fromJson(Map<String, dynamic> json) {

    avatars =

    json['avatars'] != null ? new Avatars.fromJson(json['avatars']) : null;

    nameEn = json['name_en'];

    name = json['name'];

    alt = json['alt'];

    id = json['id'];

  }

  Map<String, dynamic> toJson() {

    finalMap<String, dynamic> data = newMap<String, dynamic>();

    if (this.avatars != null) {

      data['avatars'] = this.avatars.toJson();

    }

    data['name_en'] = this.nameEn;

    data['name'] = this.name;

    data['alt'] = this.alt;

    data['id'] = this.id;

    return data;

  }

}

class Avatars {

  String small;

  String large;

  String medium;

  Avatars({this.small, this.large, this.medium});

  Avatars.fromJson(Map<String, dynamic> json) {

    small = json['small'];

    large = json['large'];

    medium = json['medium'];

  }

  Map<String, dynamic> toJson() {

    finalMap<String, dynamic> data = newMap<String, dynamic>();

    data['small'] = this.small;

    data['large'] = this.large;

    data['medium'] = this.medium;

    return data;

  }

}

五. 编辑器插件

目前也有一些AndroidStudio或者VSCode的插件,来帮助我们直接将JSON生成对应的Model

    VSCode目前没有找到比较好用的插件推荐

    Android Studio推荐FlutterJsonBeanFactory

第一步:安装插件

第二步:创建模型

    右键新建文件:

    给类起一个名字,并且将JSON复制过去

第三步:使用生成的模型

创建完成后会生成对应的模型,并且还会生成一个文件夹,里面有生成模型过程的代码

    这里不再给出,代码都是相似的

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

推荐阅读更多精彩内容