This article starts with my blog: Golang applicable dto tool, I also published the same topic in the column of the article, but the context of the article is clearer (personal feeling), link from this to the version-golang applicable DTO tool, Escape ~
The DTO (data Transfer Object) is a concept in Java that plays the role of data encapsulation and isolation. There are similar requirements in the process of developing Web applications using Golang. First post the project address Github.com/yeqown/server-common/tree/master/dbs/tools
As an example,
Now there is a user data structure as follows:
type UserModel struct {
ID int64 `gorm:" column: id "`
Name string `gorm:" column: name "`
Password string `gorm:" column: password "`
}
// Question 1: Now the requirement is that you want to return user data in JSON format and you do n’t want to include the Password field
// Solution 1:
type UserModel struct {
ID int64 `gorm:" column: id "json:" id "`
Name string `gorm:" column: name "json:" name "`
Password string `gorm:" column: password "json:"-"`
}
// Question 2: It is also in JSON data format, and you want to return the user's identity label Ident (assuming that it must be put together with user data)
// Solution 2: (This is also my scenario)
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,
}
}
Background and needs
In general, my project structure is as follows: models and services are the folders that define Data struct (UserModel) and Object (UserDTO) separately.
In fact, the process of DTO for me is to generate a new Struct structure based on Data Struct, and attach a func LoadDTOTypeFromModel (data * ModelType) * DTOType. In this process, in addition to the need for additional processing of individual Object structures, most of them are new tags ~. So this part of the work steps are similar, so why not use a tool to avoid this part of the repeated work ~?
Ideas
Let me talk about the idea first:
· 1. Get the structural description of the specified structure from .go
· 2. According to the structural description to generate a new structure
· 3. According to the additional configuration, generate a new file types.go
The structural description is as follows:
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
}
The more troublesome thing in the whole process is, how to get the structural description of a specific type of structure? Go file parsing part. Here I want to record a small episode: When I first found the method of parsing go files, I searched for "how to parse go files" in Google, and the results were not very helpful. Then I tried "How to parse .go file source" code ", the result prompts parser & loader two seemingly helpful package names. . . . Here I chose the loader.
Notes about the loader package
Package loader loads a complete Go program from source code, parsing and type-checking the initial packages plus their transitive closure of dependencies.
This package happens to load the Go program from the source code, and parse and type check the initial package.
Go file parsing part
After reading the loader package documentation, I completed a function to get the structural description information of the specified structure: the code is here
// Exported, and specified type
func 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 name
func 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
}
// The string obtained by obj.String () is as follows:
// 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
}
Among them, parseStructString is to process the string of the form type testdata.UserModel struct {Name string "gorm:" colunm: name ""; Password string "gorm:" column: password ""} and organize it into innerStruct data.
Instructions for use
go get github.com/yeqown/server-common/dbs/tools
# Get github.com/yeqown/server-common/tool.main.go,
# And selectively implement your own CustomParseTagFunc & CustomGenerateTagFunc
go build -o dbtools tool.main.go
➜ ✗ dbtools -h
Usage of ./dbtools:
-debug
Debug mode switch, additional information will be output in debug mode
-dir string
Specify the directory to be resolved
-filename string
Specify which files need to be parsed, if not set all .go files in the default dir path
-generateDir string
The directory where the generated files are stored, the default current path
-generateFilename string
Generate file name, default "types.go"
-generatePkgName string
The package name of the generated file, default "types"
-generateStructSuffix string
Replace the suffix of model struct, there is no suffix by default, such as UserSuffix => User
-modelImportPath string
Specify the import path of the model struct, such as my-server / models
-modelStructSuffix string
The model struct indicating the specific suffix needs to be parsed, default "Model"
Tool test results
./bin/dbtools \
-dir =. / dbs / tools / testdata \
-filename = type_model.go \
-generatePkgName = main \
-modelImportPath = github.com / yeqown / server-common / dbs / tools / testdata \
Reference link
loader
parser