flutter的json轉dart model問題


原文鏈接

原文鏈接

Preface

最近在做一個app,以后續用來找工作可以拿出來看看。
試試自己到產品設計能力,前后端能力等等。
中間遇到到一些有值得記錄的點全部記錄在此。

Content

json - model

本地 jsonmodel 互轉,主要用到了

  • json_serializiable
  • json_annotation
  • build_runner

meta版本依賴問題

由於使用的flutter版本是2.2.4,flutter_test與json_annotation同時依賴了不同版本的meta庫,所以不得已,沒有用上最新的json對應庫:

json_annotation: ^4.0.1,
json_serializable: ^4.1.4

類型拓展

以下是模版,在原版對基礎上新增了構造器參數,不然新版本會提醒 null safty 問題。
由於vs code老是提醒有錯誤,所以這個文件命名為template.tmpl

import 'package:json_annotation/json_annotation.dart';
%t
part '%s.g.dart';
@JsonSerializable()
class %s {
    %s(%c);

    %s
    factory %s.fromJson(Map<String,dynamic> json) => _$%sFromJson(json);
    Map<String, dynamic> toJson() => _$%sToJson(this);
}

以下是生成modeldart程序,新增了構造器參數和復雜類型的支持:

import 'dart:convert';
import 'dart:io';
import 'package:path/path.dart' as path;

const TAG = "\$";
const SRC = "./json"; //JSON 目錄
const DIST = "lib/models/"; //輸出model目錄

void walk() {
  //遍歷JSON目錄生成模板
  var src = new Directory(SRC);
  var list = src.listSync();
  var template = new File("template/template.tmpl").readAsStringSync();
  File file;
  list.forEach((f) {
    if (FileSystemEntity.isFileSync(f.path)) {
      file = new File(f.path);
      var paths = path.basename(f.path).split(".");
      String name = paths.first;
      if (paths.last.toLowerCase() != "json" || name.startsWith("_")) return;
      if (name.startsWith("_")) return;
      //下面生成模板
      var map = json.decode(file.readAsStringSync());
      //為了避免重復導入相同的包,我們用Set來保存生成的import語句。
      var set = new Set<String>();
      StringBuffer attrs = new StringBuffer();
      List<String> params = [];
      (map as Map<String, dynamic>).forEach((key, v) {
        if (key.startsWith("_")) return;
        attrs.write(getType(v, set, name));
        attrs.write(" ");
        attrs.write(key);
        attrs.writeln(";");
        attrs.write("    ");
        params.add("required this.$key");
      });
      //新增的構造器參數部分
      String conParams = "{${params.join(",")}}";
      String className = name[0].toUpperCase() + name.substring(1);
      var dist = format(template, [
        name,
        className,
        className,
        attrs.toString(),
        className,
        className,
        className
      ]);
      var _import = set.join(";\r\n");
      _import += _import.isEmpty ? "" : ";";
      dist = dist.replaceFirst("%t", _import);
      dist = dist.replaceFirst("%c", conParams);
      //將生成的模板輸出
      new File("$DIST$name.dart").writeAsStringSync(dist);
    }
  });
}

String changeFirstChar(String str, [bool upper = true]) {
  return (upper ? str[0].toUpperCase() : str[0].toLowerCase()) +
      str.substring(1);
}

//將JSON類型轉為對應的dart類型
String getType(v, Set<String> set, String current) {
  current = current.toLowerCase();
  if (v is bool) {
    return "bool";
  } else if (v is int) {
    return "int";
  } else if (v is num) {
    return "num";
  } else if (v is Map) {
    return "Map<String,dynamic>";
  } else if (v is List) {
    return "List";
  } else if (v is String) {
    //自定義類型的支持
    if (v.startsWith("$TAG") && v.endsWith("$TAG")) {
      return v.substring(1, v.length - 1);
    }
    //處理特殊標志
    if (v.startsWith("$TAG[]")) {
      var className = changeFirstChar(v.substring(3), false);
      if (className.toLowerCase() != current) {
        set.add('import "$className.dart"');
      }
      return "List<${changeFirstChar(className)}>";
    } else if (v.startsWith(TAG)) {
      var fileName = changeFirstChar(v.substring(1), false);
      if (fileName.toLowerCase() != current) {
        set.add('import "$fileName.dart"');
      }
      return changeFirstChar(fileName);
    }
    return "String";
  } else {
    return "String";
  }
}

//替換模板占位符
String format(String fmt, List<Object> params) {
  int matchIndex = 0;
  String replace(Match m) {
    if (matchIndex < params.length) {
      switch (m[0]) {
        case "%s":
          return params[matchIndex++].toString();
      }
    } else {
      throw new Exception("Missing parameter for string format");
    }
    throw new Exception("Invalid format string: " + m[0].toString());
  }

  return fmt.replaceAllMapped("%s", replace);
}

void main() {
  walk();
}

生成腳本沒有變動

dart ./lib/mo.dart
flutter packages pub run build_runner build --delete-conflicting-outputs

實際結果

普通類型

//record.json
{
    "content":"拉屎",
    "datetime":"$DateTime$",//復雜類型的傳入方式
    "cost":30,
    "useful":true
}

生成結果:

import 'package:json_annotation/json_annotation.dart';

part 'record.g.dart';
@JsonSerializable()
class Record {
    Record({required this.content,required this.datetime,required this.cost,required this.useful});

    String content;
    DateTime datetime;
    int cost;
    bool useful;
    
    factory Record.fromJson(Map<String,dynamic> json) => _$RecordFromJson(json);
    Map<String, dynamic> toJson() => _$RecordToJson(this);
}

嵌入類型

//mock.json
{
    "version":1,
    "targets":"$[]target",
    "records":"$[]record",
    "motivations":"$[]motivation"
}

生成結果

import 'package:json_annotation/json_annotation.dart';
import "target.dart";
import "record.dart";
import "motivation.dart";
part 'mock.g.dart';
@JsonSerializable()
class Mock {
    Mock({required this.version,required this.targets,required this.records,required this.motivations});

    int version;
    List<Target> targets;
    List<Record> records;
    List<Motivation> motivations;
    
    factory Mock.fromJson(Map<String,dynamic> json) => _$MockFromJson(json);
    Map<String, dynamic> toJson() => _$MockToJson(this);
}

Reference


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM