在上一篇文章中,介紹了小工具【代碼生成工具】中的任務1【解析數據字典文件】實現過程,在實現過程當中對Go語言進行了學習,具體學習的內容以下:html
咱們已經生成了schema.json文件,接下來的任務就是將模板加載進來,而後在解析這個schema.json的過程當中完成實際代碼的生成。golang
審視一下以前的代碼,有以下發現:shell
那麼,在開始新任務以前,最好是將基於不一樣的函數功能,將單文件拆分紅多文件,以增長可讀性。新功能的代碼,即可以直接再新的對應子文件中進行編寫了。json
拆分後的文件以下(終端中執行,➜ a_code_generator 是當前目錄):數組
➜ a_code_generator tree main
main
├── console.go
├── json.go
├── main.go
├── xlsx.go
└── xlsx2schema.go
複製代碼
每一個文件中包含的內容介紹以下:app
斜體與加粗的部分是比上一篇文章中有所改變的方法框架
main.go 中的 main 方法改造後的結果,以下:mongoose
package main
// 入口函數
func main() {
println("程序運行 @ 開始")
// 1.接收控制檯變量
fileName, sheetName, sheetIndex, err := receiveConsoleParam()
if err != nil {
println(err.Error())
return
}
// 2.xlsx 文件轉換成 schema 文件
xlsx2schema(fileName, sheetName, sheetIndex)
println("程序運行 @ 結束")
}
複製代碼
函數xlsx2schema的方法定義以下:函數
// xlsx 內容 轉換成 schema 文件
func xlsx2schema(fileName string, sheetName string, sheetIndex int) {
// 1.打開xlsx文件
f, err := excelize.OpenFile(fileName)
if err != nil {
println(err.Error())
return
}
// 2.若是 sheetName 爲空 或 sheetIndex 爲默認值,則打印出該文件的全部sheet頁信息
if sheetName == "" && sheetIndex == -1 {
listAllSheet(f)
return
}
// 3.對xlsx文件中的指定名稱的sheet頁逐行逐單元格進行遍歷
var rows [][]string
if sheetName != "" { // 3.1.當sheet頁名稱設置時,以 sheetName 爲準
rows = f.GetRows(sheetName)
} else { // 3.2.當sheet頁名稱未設置時,以 sheetIndex 爲準
rows = f.GetRows(f.GetSheetName(sheetIndex))
}
err = analyzeSheet(rows)
if err != nil {
println(err.Error())
return
}
}
複製代碼
將單文件拆分紅多文件以後,程序的運行命令也須要作相應的改變,以下(終端中執行,➜ a_code_generator 是當前目錄):工具
➜ a_code_generator go run main/* -f ./resource/datadict.xlsx -i 2
複製代碼
這裏涉及到的知識點以下:
模板文件實際上就是有兩部分組成的,分別是靜態內容和動態內容。靜態內容是固定不變的部分,動態內容則是在實際生成過程當中須要進行替換的部分,這一部分就是模板中的變量,通常會使用專門的模板語言進行編寫,由專門的模板引擎進行處理。經過搜索,發現了三個具備參考性的文章,以下:
經過閱讀文章,得知Go語言給咱們提供了兩個庫來處理模板(text/template和html/template),咱們要處理的模板是NodeJs的文本,使用 text/template 便可。
模板示例以下(model.tmpl):
'use strict';
const path = require("path");
const mongoose = require('mongoose');
const CommonDao = require('./commondao.js')
const dbconn = require('./index.js');
const ObjectId = mongoose.Schema.Types.ObjectId;
class Model extends CommonDao {
constructor(model) { super(model); }
getSchema() { return Schema; }
getConn() { return dbconn.mongoConn; }
getCollect() {
let file = __filename.split(path.sep);
file = file[file.length - 1].split('.')[0];
return file;
}
}
cont Schema = Model.SchemaExt({
{{.}}
});
module.exports = new Model(null);
複製代碼
最後面的 {{.}} ,即是要替換的內容,只有這一處,所以,沒有用不一樣的變量標識。點"."表明當前做用域的當前對象。
schema文件已經在前一步驟中生成出來了,接下來使用程序將其用程序打開讀取文件內容(json),並將內容反向解析成 數據字典集合,而後在集合遍歷中進行處理便可。代碼框架以下:
// 分析模式文件
func analyzeSchema(schemaPath string, modelTmplPath string, controllerTmplPath string) error {
// 1.加載 模式文件
schema, err := ioutil.ReadFile(schemaPath)
if err != nil {
return err
}
// 2.轉換 模式文件到 數據字典集合中
var dataDictSlice []DataDict // 數據字典集合
if err := Json.Unmarshal(schema, &dataDictSlice); err != nil {
return err
}
// 3.加載模板文件
modelTmpl, _ := template.ParseFiles(modelTmplPath)
controllerTmpl, _ := template.ParseFiles(controllerTmplPath)
// 4.遍歷數據字典集合
for _, dataDict := range dataDictSlice {
// 3.1.生成 model 代碼片斷
if err := generateModelFile(modelTmpl, dataDict); err != nil {
return err
}
// 3.2.生成 controller 代碼片斷
if err := generateControllerFile(controllerTmpl, dataDict); err != nil {
return err
}
}
// 5.沒有出錯,返回 nil
return nil
}
複製代碼
在遍歷過程當中,用 generateModelFile 和 generateControllerFile 承載了生成代碼文件的職責。爲了可以複用模板文件,須要將模板文件再遍歷前提早加載。analyzeSchema 中涉及到的知識點以下:
- 使用 ioutil.ReadFile 讀取 schema.json
- 使用 Json.Unmarshal 將 json 文本解析爲 strcut對象 集合
- 使用 template.ParseFiles 加載模板文件(text/template)
- 遍歷 結構體數組
在 step_01 中能夠看到 model.tmpl 的內容,須要填充的部分就是數據字典下各個字段的定義,所以,咱們只須要在程序中遍歷數據字典的字段集合,而且根據約定的規則將全部的字段信息轉換拼接成代碼片斷,而後經過模板引擎將其替換到相應的位置後,輸出成文件便可。代碼框架以下:
// 生成 model 文件
func generateModelFile(modelTmpl *template.Template, dataDict DataDict) error {
// 1.獲取 fields
fields := dataDict.Fields
// 2.將 map 的 key 升序處理
var keys []int
for key := range fields {
iKey, err := strconv.Atoi(key)
if err != nil {
return err
}
keys = append(keys, iKey)
}
sort.Ints(keys)
// 3.順序遍歷並生成 model 代碼塊
var modelScript strings.Builder
for _, key := range keys {
field := fields[strconv.Itoa(key)]
// 使用 generateFieldCode 來解析並生成每個 field 對應的代碼
modelScript.WriteString(generateFieldCode(field))
}
// 4.建立代碼文件
modelFile, err := os.Create("./dist/model/" + dataDict.Collection.Name + ".js")
if err != nil {
return nil
}
// 5.模板解析
modelTmpl.Execute(modelFile, modelScript.String())
// 6.關閉文件
defer modelFile.Close()
// 7.沒有錯誤,則返回 nil
return nil
}
複製代碼
fields是個 map,是無序的,若是須要 順序遍歷,須要將 key 轉存到數組中且進行排序後,經過遍歷key數組一一獲取 map 中的元素來實現。在遍歷過程當中,使用 generateFieldCode 來對字段基於約定的規則轉換成相應的代碼,不一樣的狀況下,此方法的邏輯是不同的,在此文中就不在敘述了。generateModelFile 中涉及到的知識點以下:
- map 的順序遍歷
- string類型轉int類型 及 int類型轉string類型
- strings.Builder 的使用方式
- 建立文件的方式
- 解析模板的方式
- defer的使用方法
不一樣的狀況下,此方法的邏輯是不同的,在此文中就再也不敘述了。
此方法與 generateModelFile 是相似的,在此文中就再也不敘述了。
結合上一篇文章,到此處,咱們已經實現了從讀取 xlsx 格式的數據字典開始,到生成 schema 文件,到最後生成代碼文件的完整過程。在這個過程當中,邊查邊寫的瞭解了Go語言的一些基本特性,雖然距離熟悉Go語言還相差甚遠,但也收穫了一個可用的代碼生成工具,也算是一個好的開頭了。