Golang the applicable DTO tool

Source: Internet
Author: User
Tags tag name


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

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

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.