如何使用 Go 的类型别名让自己的模型与 protobufs 一起工作?

Posted

技术标签:

【中文标题】如何使用 Go 的类型别名让自己的模型与 protobufs 一起工作?【英文标题】:How to use Go's type alias to make own models work with protobufs? 【发布时间】:2018-04-10 11:39:40 【问题描述】:

我有一些 REST API,我的模型定义为 Go 结构。

type User struct 
  FirstName string
  LastName  string

然后我就有了获取数据的数据库方法。

GetUserByID(id int) (*User, error)

现在我想用 https://github.com/twitchtv/twirp 替换我的 REST API。

因此我开始在 .proto 文件中定义我的模型。

message User 
  string first_name = 2;
  string last_name = 3;

现在我有两个User 类型。我们称它们为 nativeproto 类型。

我还在我的.proto 文件中定义了一个服务,它将用户返回到前端。

service Users 
  rpc GetUser(Id) returns (User);

这会生成一个我必须填写的界面。

func (s *Server) GetUser(context.Context, id) (*User, error) 
  // i'd like to reuse my existing database methods
  u, err := db.GetUserByID(id)
  // handle error
  // do more stuff
  return u, nil

不幸的是,这不起作用。我的数据库返回 native 用户,但界面需要 proto 用户。

有没有简单的方法让它工作?也许使用type aliases

非常感谢!

【问题讨论】:

如果您不希望您的数据库包依赖于 protobuf 类型,您必须手动进行转换。如果类型足够相似,例如,您可以使用 json.Marshal 和 json.Unmarshal。您应该在问题中包含生成的类型,以便我们了解它们的不同之处。 而且类型别名绝对不是解决方案。别名不会创建新类型,只会为现有类型创建新名称。 【参考方案1】:

解决问题的一种方法是手动进行转换。

type User struct 
    FirstName string
    LastName string


type protoUser struct 
    firstName string
    lastName string


func main() 
    u := db() // Retrieve a user from a mocked db

    fmt.Println("Before:")
    fmt.Printf("%#v\n", *u) // What db returns (*protoUser)
    fmt.Println("After:")
    fmt.Printf("%#v\n", u.AsUser()) // What conversion returns (User)


// Mocked db that returns pointer to protoUser
func db() *protoUser 
    pu := protoUser"John", "Dough"
    return &pu


// Conversion method (converts protoUser into a User)
func (pu *protoUser) AsUser() User 
    return Userpu.firstName, pu.lastName

关键部分是protoUser 结构上的AsUser 方法。 在那里,我们只需编写自定义逻辑,将protoUser 转换为我们想要使用的User 类型。

Working Example

【讨论】:

【参考方案2】:

正如评论部分中提到的@Peter。

我见过一个使用自定义 Convert 函数的项目。它通过json.Unmarshal 将 Protobuf 转换为本地结构,不确定性能如何,但这是一种方法。

预览代码 PLAYGROUND

// Convert converts the in struct to out struct via `json.Unmarshal`
func Convert(in interface, out interface) error 
    j, err := json.Marshal(in)
    if err != nil 
        return err
    
    err = json.Unmarshal(j, &out)
    if err != nil 
        return err
    
    return nil


func main() 
    // Converts the protobuf struct to local struct via json.Unmarshal
    var localUser User
    if err := convert(protoUser, &localUser); err != nil 
        panic(err)
    

输出

Before:
main.ProtoUserFirstName:"John", LastName:"Dough"
After:
main.UserFirstName:"John", LastName:"Dough"

Program exited.

【讨论】:

以上是关于如何使用 Go 的类型别名让自己的模型与 protobufs 一起工作?的主要内容,如果未能解决你的问题,请参考以下文章

Julia 结构中的 JSON 别名如何与 Go 中的一样?

Go语言自学系列 | golang类型定义和类型别名

20220713GO语音的结构体和面向对象

go语言

go中使用type关键字来定义类型别名

Go语言学习之旅--结构体