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());
              }
            },
    ...
    
  • 相关阅读:
    决策树
    性能测试–性能监视器
    MEAN栈开发
    Android Studio 入门(转)
    提取重复代码不应该只从代码角度,可以从业务角度看看(转)
    我眼中的领域驱动设计(转)
    网格最短路径算法(Dijkstra & Fast Marching)(转)
    Android GridView显示SD卡的图片
    Android GridView异步加载图片和加载大量图片时出现Out Of Memory问题
    Android 弹出框Dialog并缩放图片
  • 原文地址:https://www.cnblogs.com/seliote/p/14325887.html
Copyright © 2011-2022 走看看