一、json_serializable使用步驟
1.集成json_serializable
pubspec.yaml 添加以下依賴
dependencies: json_annotation: ^2.0.0 dev_dependencies: build_runner: ^1.0.0 json_serializable: ^2.0.0
添加完記得執行 flutter packages get
2.生成文件
在項目根目錄下執行 flutter packages pub run json_model
即可生成xxx.g.dart
3.解析及序列化
注意導包import 'dart:convert';
///json轉model String jsonString = '{"name": "Tony","email": "tony@example.com"}' Map userMap = json.decode(jsonString); var user = User.fromJson(userMap); ///model轉json String jsonEncode = json.encode(user); print(jsonEncode);
二、自動化生成模板
上述過程需要每次把json去生成網站去轉化成Model,接下來我們直接在本地生成,只需要寫個user.json文件再執行下命令即可。這樣每次json結構有修改后可以直接修改json文件再執行下命令即可,並且json結構能存在本地方便查看。
1.使用json_model
集成json_model
dev_dependencies: json_model: ^0.0.2 #最新版本
使用
- 在工程根目錄下創建一個名為 "jsons" 的目錄;
- 創建或拷貝Json文件到"jsons" 目錄中 ;
- 運行 pub run json_model (Dart VM工程)or flutter packages pub run json_model(Flutter中) 命令生成Dart model類,生成的文件默認在"lib/models"目錄下
具體文檔地址:https://github.com/flutterchina/json_model
2.一點點小優化
上述方式直接導入插件包已經很方便了,但使用過程中遇到了一點問題:
- 生成的model文件名和json文件名一樣,如果文件名有下划線
_
時生成的類名也是有下划線的,但我習慣使用駝峰命名 - 同上,當字段名中有下划線
_
,生成的字段也是有下划線的,要想使用駝峰命名需要手動在字段上方加上@JsonKey(name: 'user_name')
- 自動解析數據類型只支持
bool num Map List
等幾種常見類型,如果是DateTime
類型或其他類型的字段只能解析成String。 - 生成的model沒有完美的格式化,有強迫症的還得再手動格式化一下
如果你感覺這樣不友好或有自己的書寫習慣那么就自己動手吧。我這里按照自己的習慣改了一下:
- 類名,字段名駝峰命名
- 支持
DateTime
類型(后期有其他支持可以添加) - 完美的格式化
做法:不使用json_model,自己動手
- 在工程根目錄下創建一個名為 "jsons" 的目錄;
- 創建或拷貝Json文件到"jsons" 目錄中 ;
- 在項目根目錄下創建
mo.dart
文件,內容如下:
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 = "import 'package:json_annotation/json_annotation.dart';\r\n%t\npart '%s.g.dart';\r\n\r\n@JsonSerializable()\r\nclass %s {\r\n %s();\r\n\r\n %sfactory %s.fromJson(Map<String, dynamic> json) => _\$%sFromJson(json);\r\n\r\n Map<String, dynamic> toJson() => _\$%sToJson(this);\r\n}\r\n"; 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(); (map as Map<String, dynamic>).forEach((key, v) { if (key.startsWith("_")) return; /// ############################# ///處理key包含"_"時,轉為駝峰並加上@JsonKey(name="key") if (key.contains("_")) { attrs.write('@JsonKey(name: "$key")'); attrs.write("\r\n "); attrs.write(getType(v, set, name)); attrs.write(" "); attrs.write(changeToCamelCase(key, false)); attrs.writeln(";"); attrs.write("\r\n "); } else { attrs.write(getType(v, set, name)); attrs.write(" "); attrs.write(key); attrs.writeln(";"); attrs.write("\r\n "); } }); String className = ""; /// ############################# ///處理有"_"時class不是駝峰命名 if (name.contains("_")) { className = changeToCamelCase(name, true); } else { 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); //將生成的模板輸出 new File("$DIST$name.dart").writeAsStringSync(dist); } }); } /// ############################# ///轉為駝峰命名 ///big 是否大駝峰 String changeToCamelCase(String word, bool big) { if (word.contains("_")) { String result = ""; List<String> words = word.split("_"); for (var value in words) { result += (value[0].toUpperCase() + value.substring(1).toLowerCase()); } return big ? result : (result[0].toLowerCase() + result.substring(1)); } else { return big ? word[0].toUpperCase() + word.substring(1) : word[0].toLowerCase() + word.substring(1); } } 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 num) { return "num"; } else if (v is Map) { return "Map<String,dynamic>"; } else if (v is List) { return "List"; } else if (v is String) { /// ############################# ///添加DateTime類型 try { DateTime dateTime = DateTime.parse(v); if (dateTime != null) { return "DateTime"; } } catch (e) {} //處理特殊標志 if (v.startsWith("$TAG[]")) { var className = changeFirstChar(v.substring(3), false); if (className.toLowerCase() != current) { set.add('import "$className.dart"'); } /// ############################# /// 自定義model類型名字大駝峰命名 return "List<${changeToCamelCase(className, true)}>"; } else if (v.startsWith(TAG)) { var fileName = changeFirstChar(v.substring(1), false); if (fileName.toLowerCase() != current) { set.add('import "$fileName.dart"'); } /// ############################# /// 自定義model類型名字大駝峰命名 return changeToCamelCase(fileName, true); } 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(); }
這里是生成model的方法及模板,Json_model的前身,來自這里
其中注釋包含 “#############################”的地方是修改過的地方,大家要有自己的需求可以自行修改。
- 在項目根目錄下創建
mo.sh
文件,內容如下:
#!/usr/bin/env bash dart mo.dart flutter packages pub run build_runner build --delete-conflicting-outputs
這個是腳本,把命令打包后一起執行
注:如果你沒有配置dart環境變量,需要配置一下:(Mac下)
vim ~/.bash_profile
- 添加
export PATH=your flutter path/flutter/bin/cache/dart-sdk/bin:$PATH
source ~/.bash_profile
dart --version
正常顯示版本號即可
- 以上都配置完后只需一步,項目根目錄下執行
sh mo.sh
,ok,一切完美的搞定了,給大家看看結果


重要參考:
https://www.jianshu.com/p/b20514e16e10
https://book.flutterchina.club/chapter10/json_model.html