Go語言只知其一;不知其二上手記(二)

1、前言

  在上一篇文章中,介紹了小工具【代碼生成工具】中的任務1【解析數據字典文件】實現過程,在實現過程當中對Go語言進行了學習,具體學習的內容以下:html

  1. 搭建Go語言編寫環境
  2. 基本框架結構及編譯運行方式
  3. 在控制檯打印信息的方式
  4. 定義參數的方式
  5. if 的使用方法
  6. 循環的使用方法
  7. 函數的定義與使用方法
  8. 第三方模塊的使用方法
  9. xlsx文件的讀取方法(excelize)
  10. 接收命令行參數的實現方式(flag)
  11. 結構體的使用方法
  12. error的定義與使用方法
  13. 去掉首尾空字符的方法( strings.TrimSpace)
  14. Json格式的使用方法(jsoniter)
  15. 將文本輸出到文件中(ioutil)

  咱們已經生成了schema.json文件,接下來的任務就是將模板加載進來,而後在解析這個schema.json的過程當中完成實際代碼的生成。golang

2、任務二、3【加載模板、生成代碼文件】的實現

一、基礎準備

  審視一下以前的代碼,有以下發現:shell

  • 已經完成了一個相對完整的子功能
  • 根據不一樣的功能拆分出來了子函數
  • 代碼都存在於main.go文件中,文件行數已經比較多了

  那麼,在開始新任務以前,最好是將基於不一樣的函數功能,將單文件拆分紅多文件,以增長可讀性。新功能的代碼,即可以直接再新的對應子文件中進行編寫了。json

  拆分後的文件以下(終端中執行,➜ a_code_generator 是當前目錄):數組

➜  a_code_generator tree main 
main
├── console.go
├── json.go
├── main.go
├── xlsx.go
└── xlsx2schema.go
複製代碼

  每一個文件中包含的內容介紹以下:app

  • console.go
    • receiveConsoleParam // 接收控制檯變量
  • json.go
    • printJSON // 把 json 打印出來
    • writeWithIoutil // 寫入文件
  • xlsx.go
    • 結構體定義
      • DataDict // 數據字典
      • Collection // 數據集合
      • Field // 數據字段
    • listAllSheet // 輸出 xlsx 文件中全部的 sheet 頁 信息
    • setFieldInfo // 將對應表頭的內容設置到Field對應的屬性上
    • setCollectionField // 爲集合擴充字段
    • addDataDictIntoSlice // 將數據字典加入到集合中
  • xlsx2schema.go
    • analyzeSheet // 遍歷xlsx文件中某sheet頁逐行逐單元格
    • xlsx2schema // xlsx 內容 轉換成 schema 文件
  • main.go
    • main // 入口函數

斜體與加粗的部分是比上一篇文章中有所改變的方法框架

  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
複製代碼

  這裏涉及到的知識點以下:

  1. Go語言同一package下的多文件實際上和一個文件同樣,便量、函數等都可以隨意訪問
  2. 因爲分紅了多個文件,在運行或編譯main.go時,必定要保證包內的其餘文件也都進行了編譯,如 go run main/*
  3. 程序運行,必須有一個main包,運行時會去main包下找main入口方法,所以,對於可執行的Go程序,main方法在main包下應該只有一個且不必定非要在main.go文件中

二、開始動手

step_01:設計模板文件

  模板文件實際上就是有兩部分組成的,分別是靜態內容和動態內容。靜態內容是固定不變的部分,動態內容則是在實際生成過程當中須要進行替換的部分,這一部分就是模板中的變量,通常會使用專門的模板語言進行編寫,由專門的模板引擎進行處理。經過搜索,發現了三個具備參考性的文章,以下:

  經過閱讀文章,得知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);
複製代碼

最後面的 {{.}} ,即是要替換的內容,只有這一處,所以,沒有用不一樣的變量標識。點"."表明當前做用域的當前對象。

step_02:建立代碼文件 template.go

step_03:在 template.go 中添加解析 schema 的方法

  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 中涉及到的知識點以下:

  1. 使用 ioutil.ReadFile 讀取 schema.json
  2. 使用 Json.Unmarshal 將 json 文本解析爲 strcut對象 集合
  3. 使用 template.ParseFiles 加載模板文件(text/template)
  4. 遍歷 結構體數組

step_04:在 template.go 中實現 generateModelFile

  在 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 中涉及到的知識點以下:

  1. map 的順序遍歷
  2. string類型轉int類型 及 int類型轉string類型
  3. strings.Builder 的使用方式
  4. 建立文件的方式
  5. 解析模板的方式
  6. defer的使用方法

step_05:在 template.go 中實現 generateFieldCode

  不一樣的狀況下,此方法的邏輯是不同的,在此文中就再也不敘述了。

step_06:在 template.go 中實現 generateControllerFile

  此方法與 generateModelFile 是相似的,在此文中就再也不敘述了。

step_07:在 main 方法中,調用 analyzeSchema ,把功能串接起來

3、總結

  結合上一篇文章,到此處,咱們已經實現了從讀取 xlsx 格式的數據字典開始,到生成 schema 文件,到最後生成代碼文件的完整過程。在這個過程當中,邊查邊寫的瞭解了Go語言的一些基本特性,雖然距離熟悉Go語言還相差甚遠,但也收穫了一個可用的代碼生成工具,也算是一個好的開頭了。

相關文章
相關標籤/搜索