Flutter的Json和Model转换

  • 概述

    网络传输的数据通常都是通过json格式来定义的,在Android原生开发中,因为我们操作的是对象,所以我们通常会建立实体bean来给数据建模,而实体bean和json的互相转换通常交给Gson来做,Gson的原理就是通过反射来获取类里面的Field,然后读取或者设置它们。

    而在Flutter中,因为运行时反射会干扰 Dart 的 tree shaking,使用tree shaking,可以在 release 版中“去除”未使用的代码,这可以显著优化应用程序的大小。由于反射会默认应用到所有代码,因此tree shaking 会很难工作,因为在启用反射时很难知道哪些代码未被使用,因此冗余代码很难剥离,所以 Flutter 中禁用了 Dart 的反射功能,而正因如此也就无法实现动态转化 Model 的功能。

  • 基本思路

    正是因为禁用了运行时反射,所以我们的转换方向只能回到针对某个具体的类的属性来进行一一赋值的做法,比如:

    import 'dart:convert';
    //类中定义序列化的相关方法,把属性一一赋值
    class User {
      final String name;
      final String email;
    
      User(this.name, this.email);
    
      User.fromJson(Map json)
          : name = json['name'],
            email = json['email'];
    
      Map toJson() =>
        {
          'name': name,
          'email': email,
        };
    }
    //转换demo
    {
      "name": "John Smith",
      "email": "[email protected]"
    }
    //json是dart:convert库中的一个JsonCodec类型的对象,通过它可以把json字符串和Map相互转化
    Map user = json.decode(json);
    var user = User.fromJson(userMap);
    

    打眼一看就会觉得这种方式对于开发者来说太繁琐了,所以我们需要通过一些工具简化它。

  • json_serializable

    虽然我们不能在运行时通过反射来获取类的信息,但是在运行前的开发阶段我们可以获取类文件,通过检索类文件的方式来获取类信息,因为dart类的定义规则是确定的,所以我们可以做到这件事。既然可以拿到类信息,我们就可以把序列化的代码用模版的方式创造出来,然后我们的mode类中就可以直接通过它来完成序列化,下面看一下使用:

    首先需要添加json_serializable的依赖,总共需要三个,分别是json_annotation(这个需要添加到dependencies下,因为注解存在于model类,model类也是需要打包到程序中的)、build_runner和json_serializable,后两者需要添加到dev_dependencies下,因为这两个是用来生成序列化代码文件的工具,只在开发阶段有用,所以不需要打包到正式依赖中。

    在这里贴一下快速添加依赖的命令,可以不需要关注最新版本号,命令执行后会自动下载并加到pubspec.yaml中:

    //pubspec.yaml中自动根据最新版本号添加依赖命令:
    添加到dependencies中:flutter pub add  xxx(库名)
    添加到dev_dependencies中:flutter pub add -d  xxx(库名)
    

    添加依赖后:

    import 'package:json_annotation/json_annotation.dart';
    
    /// xxx.g.dart的xxx必须和文件名一致,自动生成完成前前面不能加包前缀,它会在和模版类同级目录下自动生成,生成后可以移动到其他目录,这个时候才可以加路径前缀
    part 'json_g_dart/animal_template.g.dart';
    ///
    ///@author mph
    ///@date 2022/3/10
    
    ///json_serializable只是把序列化的工作抽到一个xxx.g.dart文件中,模版类,即当前类这样的,还是需要手动创建,这部分工作可以通过一些IDE插件来完成,或者自己写一个脚本生成
    @JsonSerializable()
    class Animal{
      String kind;
      String color;
      Animal(this.color,this.kind);
    
      //必须是"_$类名FromJson"和"_$类名ToJson"的格式
      factory Animal.fromJson(Map json) => _$AnimalFromJson(json);
      Map toJson() => _$AnimalToJson(this);
    }
    

    可以看到,这里的model类中fromJson构造方法和toJson方法可以直接调用模版类的序列化方法:

    part of '../animal_template.dart';
    
    // **************************************************************************
    // JsonSerializableGenerator
    // **************************************************************************
    
    Animal _$AnimalFromJson(Map json) => Animal(
          json['color'] as String,
          json['kind'] as String,
        );
    
    Map _$AnimalToJson(Animal instance) => {
          'kind': instance.kind,
          'color': instance.color,
        };
    

    这样一来,我们就不需要写繁琐的赋值代码了,在model类的属性很多的时候优势很明显。

    但是依然有一点不好,就是我们仍然需要手动创建model类,而model类通常和实际中的数据json是对应的,在json数据很复杂的时候,手动创建依然不是很友好。

  • 自动化创建model类

    其实这里和Android原生是一样的了,因为原生开发中我们也是需要手动建类,那里我们有Java Json Convertor这样优秀的IDE插件可以帮我们完成这个工作,同样,在Flutter的光明前景下,也有很多类似的插件应运而生,比如JsonToDart,他可以完成Java和Json间转换同样的工作,不过他转换的是dart的类而已,连使用方法都是一样的:在需要建立model类的文件夹右键选择“JsonToDart”选项,然后贴入需要转换的json示例即可。

    其实插件的原理和上面json_serializable的思路是一致的,先创建一个类文件,也就是model的类文件,通过扫描json字符串,然后根据dart类的语法来向这个文件中写入代码,就这么简单。

    我们也可以写一个自己的脚本,然后自己实现插件的功能,或者结合json_serializable来抽出序列化的逻辑,不过思路是简单的,实现过程可能有很多细节,比如嵌套类的生成等。

你可能感兴趣的:(Flutter的Json和Model转换)