Golang适用的DTO工具

栏目: Go · 发布时间: 6年前

内容简介:本文首发于我的博客:DTO (Data Transfer Object) 是Java中的概念,起到数据封装和隔离的作用。在使用Golang开发Web应用的过程中,也会有类似的需求。先贴项目地址现在有一个用户数据结构如下,

本文首发于我的博客: 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)的文件夹。

Golang适用的DTO工具

其实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 文件方法的时候,在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 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
            }
            // 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 & CustomGenerateTagFunc
go build -o dbtools tool.main.go

➜ ✗ dbtools -h
Usage 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 \

Golang适用的DTO工具

Golang适用的DTO工具

参考链接


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

高可用架构(第1卷)

高可用架构(第1卷)

高可用架构社区 / 电子工业出版社 / 2017-11-1 / 108.00元

《高可用架构(第1卷)》由数十位一线架构师的实践与经验凝结而成,选材兼顾技术性、前瞻性与专业深度。各技术焦点,均由极具代表性的领域专家或实践先行者撰文深度剖析,共同组成“高可用”的全局视野与领先高度,内容包括精华案例、分布式原理、电商架构等热门专题,及云计算、容器、运维、大数据、安全等重点方向。不仅架构师可以从中受益,其他IT、互联网技术从业者同样可以得到提升。一起来看看 《高可用架构(第1卷)》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

随机密码生成器
随机密码生成器

多种字符组合密码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试