美文网首页FlutterflutterFlutter
[Flutter] 08-Flutter中的Json转Model

[Flutter] 08-Flutter中的Json转Model

作者: codeTao | 来源:发表于2020-06-23 16:38 被阅读0次

    背景: 在开发中,服务端通常返回Json数据,我们需要将Json数据转模型对象来使用。一般情况下,我们会使用一些第三方库来动态转化Model,但是Flutter中没有像Java的Gson/Jackson这类Json序列化类库,因为Flutter中禁用运行时反射。官方解释是运行时反射会干扰Dart的tree shaking,使用tree shaking可以在release版中去除未使用的代码,这可以显著优化应用程序的大小。由于反射会默认应用到所有代码,因此tree shaking会很难工作,因为在启用反射时很难知道哪些代码未被使用,因此冗余代码很难剥离,所以Flutter中禁用了Dart的反射功能,而正因如此也就无法实现动态转化Model的功能。

    在此基础上,接下来我们看下Flutter中还有哪几种Json转模型的方式:

    一. 手动转化

    在上篇[Flutter] 07-Flutter中反序列化Json已经通过6个示例分析过了, 这里不再讨论。

    二. json_serializable

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

    • 一个自动化源代码生成器来为你生成 JSON 序列化数据模板;
    • 由于序列化数据代码不再需要手动编写或者维护,你可以将序列化 JSON 数据在运行时的异常风险降到最低;

    第1步:添加相关的依赖

    依赖分为项目依赖(dependencies),开发依赖(dev_dependencies),在pubspec.yaml中添加如下依赖:

    dependencies:
      json_annotation:^3.0.1
    
    dev_dependencies:
      json_serializable:^3.2.5
      build_runner:^1.8.0
    
    • 注意:添加后需要执行flutter pub get确保我们的项目中有这些依赖。
    • 注意:yaml配置文件对于缩进要求十分严格,下面的build_runnerjson_serializable应该是与flutter_test平级的,千万不要写在flutter_test缩进后,这样它会认为这两个是flutter_test的子集目录!

    由于很多朋友在这一步遇到了问题,这里贴出源码:

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

    • 根据下面简单Json数据创建模型类:
    final jsonInfo = {
      "nickname": "coderTao",
      "age": 20,
      "courses": ["政治", "高数", "英语"],
      "register_date": "2018-2-22",
      "computer": {
        "brand": "MackBook",
        "price": 9999
      }
    };
    
    • User类的代码:
    // 1.import 导入json_annotation.dart
    import 'package:json_annotation/json_annotation.dart';
    import 'computer_model.dart';
    
    // 2.user.g.dart 将在我们运行生成命令后json_serializable帮我们自动生成.g.dart文件,在未执行命令前该行可能会报错
    part 'user_model.g.dart';
    
    // 3.这个标注是告诉生成器,这个类是需要生成Model类的
    @JsonSerializable()
    class User {
      String name;
      int age;
      //显式关联JSON字段名与Model属性的对应关系,
      // 如下将属性registerDate和register_date字段关联
      @JsonKey(name: "register_date")
      String registerDate;
      List<String> courses;
      Computer computer;
      // 4.必须的构造方法
      User(this.name, this.age, this.registerDate, this.courses, this.computer);
      // 5.必须有的对应工厂构造器
      factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
      Map<String, dynamic> toJson() => _$UserToJson(this);
    
      //这里 toString方法不是必须的, 只是用测试数据
      @override
      String toString() {
        return'User{name: $name, age: ${age}, registerDate: $registerDate, courses: $courses, computer: $computer}';
      }
    }
    
    • Computer类的代码:
    // 1.import 导入json_annotation.dart
    import 'package:json_annotation/json_annotation.dart';
    
    // 2.computer.g.dart 将在我们运行生成命令后json_serializable帮我们自动生成.g.dart文件,在未执行命令前该行可能会报错
    part 'computer.g.dart';
    
    // 3.这个标注告诉json_serializable哪一个类需要进行转换生成Model类
    @JsonSerializable()
    class Computer {
      String brand;
      double price;
      //4.必须的构造方法
      Computer(this.brand, this.price);
      //5.必须有的对应工厂构造器
      factory Computer.fromJson(Map<String, dynamic> json) => _$ComputerFromJson(json);
      Map<String, dynamic> toJson() => _$ComputerToJson(this);
        
      //这里 toString方法不是必须的, 只是用测试数据
      @override
      String toString() {
        return'Computer{brand: $brand, price: $price}';
      }
    }
    

    最后总结一下以json_serializable 的方式创建模型类必须5步:

    • 1.import 导入json_annotation.dart
    import 'package:json_annotation/json_annotation.dart';
    
    • 2.json_serializable根据当前类,以part 类名.g.dart格式生成的文件。
      以user.dart为例如下:
    part 'user.g.dart';
    
    • 3.在class上标注 @JsonSerializable() 告诉json_serializable哪一个类需要进行转换生成Model类。
    • 4.创建必须的构造方法。
    • 5.创建必须的对应的工厂构造器。

    备注1:
    第五步实际就是创建两个方法:

    • 提供一个工厂构造方法User.fromJson,该方法实际调用生成文件的UserFromJson方法进行反序列化。
    • 提供一个toJson()序列化对象的方法,实际调用生成文件的_$UserToJson()方法,并将调用对象解析生成Map<String ,dynamic>。

    备注2:

    • _$UserFromJson(json) : 它接收了一个map:Map<String, dynamic>,并将这个Map里的值映射为我们所需要的实体类对象。我们就可以使用这个方法,将存有json数据的map转化为我们需要的实体类对象。
    • _$UserToJson(this): 将调用此方法的对象直接根据字段映射成Map。
      而这两个都是私有方法,part让两个文件共享作用域与命名空间,所以我们需要将生成的方法暴露给外部。

    备注3:
    UserFromJson(json)ToJson()调用方法,在未执行生成对应的.g.dart文件指令前该行可能会报错。

    part 'computer.g.dart';part 'user.g.dart'; ,在未执行生成对应的.g.dart文件指令前该行可能会报错。

    备注4:
    toString方法不是必须的,只用来打印输出进行测试。

    第3步:生成对应的.g.dart文件指令

    该操作有两种指令:一次性生成指令和 持续性生成指令。

    一次性生成指令

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

    flutter pub run build_runner build
    
    • 该指令是一次性生成JSON序列化的代码。 该指令通过我们的源文件,找出需要生成Model类的源文件(包含@JsonSerializable标注的)来生成对应的.g.dart文件。建议将所有Model类放在一个单独的目录下,然后在该目录下执行命令。

    持续性生成指令

    如果感觉每次更改Model时都需要执行一次性生成指令比较繁琐,这时可以使用下面的持续生成指令:

    flutter pub run build_runner watch
    

    在项目根目录下运行该指令后会启动观察器, 观察器可以监视我们项目中文件的变化,并在需要时自动构建必要的文件。只需启动一次观察器,然后它就会在后台运行,这种方式也很安全。

    第4步:测试并打印

    final jsonInfo = {
      "nickname": "coderTao",
      "age": 20,
      "courses": ["政治", "高数", "英语"],
      "register_date": "2018-2-22",
      "computer": {
        "brand": "MackBook",
        "price": 9999
      }
    };
    
    final user = User.fromJson(jsonInfo);
    print(user);
    

    三. 网页转换

    app.quicktype.io 是一个将JSON转换成模型类的工具网站,目前来看支持大部分常用语言,并且灵活的可选项也非常多:

    优点: 这种方式操作起来会比使用json_serializable操作起来更简便一些,并且带下划线字段会自动转换为驼峰命名的属性名。
    缺点: 如果数据过于复杂的话,在生成的时候可能会少了某一个类,并且不能进行父类抽取。

    四. 编辑器插件

    目前Android Studio(或IntelliJ)有几个插件,可以将json文件转成Model类,但插件质量参差不齐,甚至还有一些有抄袭嫌疑,故笔者在此不做优先推荐,读者有兴趣可以自行了解。

    Json转Model几种方式总结:

    • 手动序列化JSON:比较麻烦,效率低,但新手还是多做尝试和了解比较好。
    • json_serializable:效率高,watch很好用。
    • 工具网站:效率高,更多功能可选。

    总体推荐使用后两种,可以大大提升开发效率,不用埋头去搞一些重复的序列化工作。

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

    相关文章

      网友评论

        本文标题:[Flutter] 08-Flutter中的Json转Model

        本文链接:https://www.haomeiwen.com/subject/kjikxktx.html