zoukankan      html  css  js  c++  java
  • Flutter JSON 解析

    原理

    因为 tree shaking,Flutter 禁用了 Dart 的反射,Flutter 禁用了 Dart 的反射,Flutter 禁用了 Dart 的反射,重要的事情说三遍,所以 Flutter 无法用 JSON decode 泛型 model,解决方法是使用 json_serializable 把响应基类先 decode,并把响应数据类型 decode 为 Map<String, dynamic>,在需要使用响应数据类型的地方再调用实际类型的 fromJson 序列化。

    HTTP 接口

    接口请求参数样例为,请求方式为 POST Request Body:{"countryCode": "86", "telNo": "13227753101"}
    接口响应参数样例为:{"code": 0, "msg": "ok", "data": {"registered": true}}

    Flutter 解析

    依赖 pubspec.yaml

    ...
    dependencies:
      ...
      http: ^0.12.2
      json_annotation: ^3.1.1
    
    dev_dependencies:
      ...
      build_runner: ^1.11.0
      json_serializable: ^3.5.1
    ...
    

    编译前置脚本 build.sh

    #!/bin/bash
    
    ############################################################################
    ## 项目编译前置脚本
    ## 使用:chmod +x build.sh && ./build.sh
    ############################################################################
    
    echo 'Start build music_hub_frontend'
    
    # 拉取依赖
    flutter pub get
    
    # 生成 model 类
    cd lib/model/
    flutter packages pub run build_runner build
    cd -
    
    echo 'Build success'
    

    所有 model 类均放置在 lib/model 下。

    响应基类 lib/model/resp.dart

    import 'package:json_annotation/json_annotation.dart';
    
    part 'resp.g.dart';
    
    /// 接口响应模型基类
    /// json_serializable 不支持泛型
    /// 所以只能把实际数据丢成 Map<String, dynamic> 在实际使用的地方再解析一遍
    ///
    /// @author seliote
    /// @since 2021-01-25
    @JsonSerializable()
    class Resp {
      // 请求响应状态码,不可为 null
      @JsonKey(name: "code")
      int code;
    
      // 请求响应状态码描述,不可为 null
      @JsonKey(name: "msg")
      String msg;
    
      // 请求响应实际数据,可能为 null
      @JsonKey(name: "data")
      Map<String, dynamic> data;
    
      Resp(this.code, this.msg, this.data);
    
      factory Resp.fromJson(Map<String, dynamic> json) => _$RespFromJson(json);
    
      Map<String, dynamic> toJson() => _$RespToJson(this);
    
      /// 判断响应对应的操作是否成功
      bool isSuccess() => code == 0;
    
      /// 获取响应的显示信息
      String displayMsg() {
        // 没用国际化
        switch (code) {
          case 0:
            return "成功";
          case -1000:
            return "未知异常";
          case -1001:
            return "请求地址异常";
          case -1002:
            return "请求参数异常";
          default:
            return "信息未配置";
        }
      }
    }
    

    响应数据类型 lib/model/user/registered_status.dart

    import 'dart:core';
    
    import 'package:json_annotation/json_annotation.dart';
    
    part 'registered_status.g.dart';
    
    /// /user/registered_status 实体类
    ///
    /// @author seliote
    /// @since 2021-01-23
    
    /// /user/registered_status 请求实体类
    @JsonSerializable()
    class RegisteredStatusReq {
      @JsonKey(name: "country_code")
      String countryCode;
    
      @JsonKey(name: "tel_no")
      String telNo;
    
      RegisteredStatusReq(this.countryCode, this.telNo);
    
      factory RegisteredStatusReq.fromJson(Map<String, dynamic> json) =>
          _$RegisteredStatusReqFromJson(json);
    
      Map<String, dynamic> toJson() => _$RegisteredStatusReqToJson(this);
    }
    
    /// /user/registered_status 响应实体类
    @JsonSerializable()
    class RegisteredStatusResp {
      @JsonKey(name: "registered")
      bool registered;
    
      RegisteredStatusResp(this.registered);
    
      factory RegisteredStatusResp.fromJson(Map<String, dynamic> json) =>
          _$RegisteredStatusRespFromJson(json);
    
      Map<String, dynamic> toJson() => _$RegisteredStatusRespToJson(this);
    }
    

    项目根目录下执行编译前置脚本:chmod +x build.sh && ./build.sh,此时 JSON 解析格式已经完成,接下来是使用部分。

    HTTP 请求封装 lib/util/backend_utils.dart

    import 'dart:convert';
    
    import 'package:frontend/model/resp.dart';
    import 'package:http/http.dart' as http;
    
    /// 后台相关工具
    ///
    /// @author seliote
    /// @since 2021-01-23
    
    const String SCHEME = "http";
    // 服务器应用部署 URL
    const String SERVER_URL = "localhost";
    // 服务器端口
    const int SERVER_PORT = 80;
    
    /// Post 方式发送 request body
    /// 
    /// [path] URL 文件路径部分
    /// [reqBody] POST 请求实体部分
    Future<Resp> post<T>(String path, Object reqBody) async {
      Map<String, String> headers = {"Content-Type": "application/json"};
      var url = "$SCHEME://$SERVER_URL:$SERVER_PORT$path";
      var resp = await http.post(url, headers: headers, body: json.encode(reqBody));
      var respModel = Resp.fromJson(json.decode(resp.body));
      return respModel;
    }
    

    实际请求代码:

    ...
        var next = FlatButton(
            onPressed: () async {
              var resp = await post<RegisteredStatusResp>(
                  "/user/registered_status",
                  RegisteredStatusReq(
                      countryCode.data, textEditingController.text));
              if (resp.isSuccess()) {
                print("is registered: "
                    "${RegisteredStatusResp.fromJson(resp.data).registered}");
              } else {
                print(resp.displayMsg());
              }
            },
    ...
    
  • 相关阅读:
    [LeetCode] Power of Three 判断3的次方数
    [LeetCode] 322. Coin Change 硬币找零
    [LeetCode] 321. Create Maximum Number 创建最大数
    ITK 3.20.1 VS2010 Configuration 配置
    VTK 5.10.1 VS2010 Configuration 配置
    FLTK 1.3.3 MinGW 4.9.1 Configuration 配置
    FLTK 1.1.10 VS2010 Configuration 配置
    Inheritance, Association, Aggregation, and Composition 类的继承,关联,聚合和组合的区别
    [LeetCode] Bulb Switcher 灯泡开关
    [LeetCode] Maximum Product of Word Lengths 单词长度的最大积
  • 原文地址:https://www.cnblogs.com/seliote/p/14325887.html
Copyright © 2011-2022 走看看