Flutter JSON 解析

原理

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

REST 接口

REST 接口请求参数样例为,请求方式为 POST Request Body:{"countryCode": "86", "telNo": "13227753101"}
REST 接口响应参数样例为:{"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() {
    return 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
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(RegisteredStatusResp.fromJson(resp.data).toJson());
          } else {
            print(resp.displayMsg());
          }
        },
...
上一篇:URI is not registered (Settings | Languages & Frameworks | Schemas and DTDs)


下一篇:【django-simpleui】‘simpletags‘ is not a registered tag library报错的解决方法