JSONExport 原始碼解析
因為最近想開發一款網路的大禮包,類似Mock API 到Model 的生成,通過一個按鈕,或者一行命令去完成,來提高開發效率,節省重複勞動力,畢竟我們開發所有輪子的目的就在於此。
其中不免需要用到程式碼生成檔案之類的功能,那如何生成呢?
答案有很多,不過大抵都是通過程式碼來生成程式碼,也有一些優秀的開源庫和工具,接下來就介紹我們今天的主角JSONExport (https://github.com/Ahmed-Ali/JSONExport),由作者[Ahmed-Ali](https://github.com/Ahmed-Ali)開發提供的優秀開源庫。
它是一款Mac的App,通過Swift語言來編寫,需要在網站上下載工程用Xcode編譯執行。
執行後,大致是長這樣:
左邊輸入JSON,右邊生成檔案預覽,右下角根據選擇的語言,通過方法loadSupportedLanguages()來讀取本地模版。
作者在工程中定義了一系列的資料模版來將JSON來對映解析,類似如這樣:
{ "langName": "名稱", // 模版名稱 "modelStart": "", "basicTypesWithSpecialFetchingNeedsReplacements": [], // 型別轉換對映 "basicTypesWithSpecialStoringNeeds": [], // 基礎型別對映 "importForEachCustomType": "", "reservedKeywords": [], //關鍵字轉換 "briefDescription": "", //生成的描述 "utilityMethods": [], "dataTypes": {}, // 基礎型別對映 "wordsToRemoveToGetArrayElementsType": [], "constructors": [], "constVarDefinition": "", "modelDefinition": "", "genericType": "", "headerFileData": { "modelDefinitionWithParent": "", "modelEnd": "", "instanceVarDefinition": "", "utilityMethodSignatures": [], "constructorSignatures": [], "typesNeedSpecialDefinition": [], "modelStart": "", "importParentHeaderFile": "", "headerFileExtension": "", "modelDefinition": "", "importForEachCustomType": "", "instanceVarWithSpeicalDefinition": "", "staticImports": "#import <UIKit/UIKit.h>" }, // OC標頭檔案 "fileExtension": "", // 檔案字尾,匯出檔案拼接用 "arrayType": "NSArray", "basicTypesWithSpecialFetchingNeeds": [], "displayLangName": "", //客戶端顯示的語言名稱,如:Swift,Objective-C "instanceVarDefinition": "", "supportsFirstLineStatement": "", "modelEnd": "", "staticImports": "", // 需要匯入的靜態包 "importHeaderFile": "" }
流程大致是這樣:
模版中有需要一些動態插入的部分,如屬性名,類名等之類的,用了<!ModelName!>,<!VarName!>作為關鍵次,在插入字串過程中,會對映替換JSON的內容。可以看下SharedConstants.swift 檔案中:
let elementType = "<!ElementType!>" let modelName = "<!ModelName!>" let modelWithParentClassName = "<!ParentClass!>" let varName = "<!VarName!>" let capitalizedVarName = "<!CapitalizedVarName!>" let varType = "<!VarType!>" let varTypeReplacement = "<!VarBasicTypeReplacement!>" let varTypeCast = "<!VarBasicTypeCast!>" let capitalizedVarType = "<!CapitalizedVarType!>" let lowerCaseVarType = "<!LowerCaseVarType!>" let lowerCaseModelName = "<!LowerCaseModelName!>" let jsonKeyName = "<!JsonKeyName!>" let constKeyName = "<!ConstKeyName!>" let additionalCustomTypeProperty = "<!AdditionalForCustomTypeProperty!>"
在上面的檔案中基本是定義了大量關鍵詞,從這裡不難看出,其中的屬性與JSON的模版檔案來進行對映關係的,使插入的內容能夠動態去生成。
在生成字串的過程中,是逐行逐行來寫入檔案,就像我們的印表機一樣一行一行來輸出,用換行符\n和\t來進行格式排版的控制,最後替換關鍵詞,這樣的思路確實是能夠勝任程式碼生成程式碼的部分。
可以知道的是在其中作者用一套解析方式,應用於多個模版,如果生成的檔案需要增加的同時,也需要增加一個模版。下面具體來解析一下原始碼中的程式碼設計,整個思路大致是這樣,但是作者使用的設計模式還是值得學習的。
是不是現在有點疑惑,各種模型生成的方式不一樣Swift,Objective-C,Java,怎麼用一套規則去解析生成呢。那麼我們就瞭解下原始碼中的一些程式碼設計方式。首先看一下一些類的結構,大致分為三組,也可以定義為三層:
- Supported Languages(模版檔案) => Data層
- Lang Data Models (模版檔案模型)=> Model層
- File content generators (檔案生成轉化類) => Service層