Golang適用的DTO工具

來源:互聯網
上載者:User

本文首發於我的部落格:Golang適用的DTO工具,我同時在知乎專欄也發布了同樣主題的文章,但是文章脈絡更清晰一點(個人感覺),連結由此去知乎版本-Golang適用的DTO工具,逃~

DTO (Data Transfer Object) 是Java中的概念,起到資料封裝和隔離的作用。在使用Golang開發Web應用的過程中,也會有類似的需求。先貼項目地址 github.com/yeqown/server-common/tree/master/dbs/tools

舉個例子

現在有一個使用者資料結構如下,

type UserModel struct {    ID          int64   `gorm:"column:id"`    Name        string  `gorm:"column:name"`    Password    string  `gorm:"column:password"`}

// 問題1: 現在要求是想要JSON格式返回使用者資料,並且不希望其中包含有Password欄位
// 解決1:

type UserModel struct {    ID          int64   `gorm:"column:id" json:"id"`     Name        string  `gorm:"column:name" json:"name"`    Password    string  `gorm:"column:password" json:"-"`}

// 問題2: 同樣是JSON資料格式,並且希望額外返回使用者的身份標示Ident(假設必須要跟使用者資料放在一起)
// 解決2: (這也是我的情境)

type UserDTO struct {    ID          int64   `json:"id"`     Name        string  `json:"name"`    Password    string  `json:"-"`    Ident       string  `json:"ident"`}func LoadUserDTOFromModel(data *UserMolde) *UserDTO {    ident := genUserIdent(data)    return &{        ID          data.ID,        Name        data.Name,        Ident       ident,    }}

背景和需求

一般來說我的項目結構如下:其中models和services也就是分開定義Data struct(UserModel)和Object(UserDTO)的檔案夾。

其實DTO的過程對於我來說,就是基於Data Struct產生一個新的Struct結構,並附帶一個func LoadDTOTypeFromModel(data *ModelType) *DTOType。在這個過程中,其實除了個別Object結構體需要額外處理以外,大部分都是新換一個tag~。因此這部分工作步驟都是類似的,那麼為什麼不用一個工具來避免這部分重複的工作呢~?

思路

先說一下思路:

· 1. 從.go中擷取到指定結構體的結構性描述
· 2. 根據結構性描述來產生新的結構體
· 3. 根據額外的配置,產生一個新的檔案types.go

其中結構性描述如下:

type innerStruct struct {    fields  []*field    content string    name    string    pkgName string}type field struct {    name string // field name string    typ  string // field type string    tag  string // tag name string}

在整個流程中比較麻煩的就是,怎麼擷取到,特定類型結構體的結構性描述?Go檔案解析部分。這裡想記錄一個小插曲:最開始我找解析go檔案方法的時候,在Google中搜尋“如何解析go檔案”,出來的結果沒有太大協助,然後我又嘗試了“How to parse .go file source code”,結果就提示了parser & loader 兩個看起來就很有協助的包名。。。。這裡我選用了loader

關於loader包的說明

Package loader loads a complete Go program from source code, parsing and type-checking the initial packages plus their transitive closure of dependencies.

正好這個包是從原始碼去載入Go程式,對初始包進行解析和類型檢查等。

Go檔案解析部分

經過閱讀loader包文檔,我完成了一個函數用於擷取指定的結構體的結構性描述資訊:代碼在此

// Exported, and specified typefunc loadGoFiles(dir string, filenames ...string) ([]*innerStruct, error) {    newFilenames := []string{}    for _, filename := range filenames {        newFilenames = append(newFilenames, path.Join(dir, filename))    }    conf.CreateFromFilenames("", newFilenames...)    prog, err := conf.Load()    if err != nil {        log.Println("load program err:", err)        return nil, err    }    return loopProgramCreated(prog.Created), nil}// loopProgramCreated to loo and filter:// 1. unexported type// 2. bultin types// 3. only specified style struct namefunc loopProgramCreated(    created []*loader.PackageInfo,) (innerStructs []*innerStruct) {    for _, pkgInfo := range created {        pkgName := pkgInfo.Pkg.Name()        defs := pkgInfo.Defs        for indent, obj := range defs {            if !indent.IsExported() ||                obj == nil ||                !strings.HasSuffix(indent.Name, specifiedStructTypeSuffix) {                continue            }            // obj.String() 得到的string如:            // type testdata.UserModel struct{Name string "gorm:\"colunm:name\""; Password string "gorm:\"column:password\""}            is := parseStructString(obj.String())            is.pkgName = pkgName            is.pureName()            if isDebug {                log.Println("parse one Model: ", is.name, is.pkgName, is.content)            }            innerStructs = append(innerStructs, is)        }    }    return}

其中parseStructString是對形如type testdata.UserModel struct{Name string "gorm:"colunm:name""; Password string "gorm:"column:password""}的字串進行處理並整理成為innerStruct資料。

使用說明

go get github.com/yeqown/server-common/dbs/tools# 擷取 github.com/yeqown/server-common/tool.main.go, # 並選擇性的實現自己的 CustomParseTagFunc & CustomGenerateTagFuncgo build -o dbtools tool.main.go➜ ✗ dbtools -hUsage of ./dbtools:  -debug        偵錯模式開關,偵錯模式下會輸出額外的資訊  -dir string        指定需要解析的目錄  -filename string        指定哪些檔案需要被解析,如果未設定預設dir路徑下所有的.go檔案  -generateDir string        組建檔案存放的目錄,預設當前路徑  -generateFilename string        組建檔案名,預設"types.go"  -generatePkgName string        組建檔案的包名,預設"types"  -generateStructSuffix string        替換model struct的尾碼,預設無尾碼,如UserSuffix => User  -modelImportPath string        指明model struct的匯入路徑, 如my-server/models  -modelStructSuffix string        指明特定尾碼的model struct需要被解析,預設"Model"

工具測試結果

./bin/dbtools \-dir=./dbs/tools/testdata \-filename=type_model.go \-generatePkgName=main \-modelImportPath=github.com/yeqown/server-common/dbs/tools/testdata \


參考連結

  • loader
  • parser
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.