Go – Idiomatic way to map one struct to another

huangapple go评论206阅读模式
英文:

Go - Idiomatic way to map one struct to another

问题

我正在使用一个第三方的Go语言库来查询LDAP用户数据库。该库返回一个SearchResult切片,其中包含我需要映射到自己的User结构体的ResultUser。这两个结构体具有不同的字段名,而且我只需要ResultUser中的特定字段。在Go语言中,是否有更符合惯用方式的方法将一个结构体转换为另一个结构体呢?

我在下面创建了一个演示示例(链接也在Go Playground上)。非常感谢您对这个Go语言新手能给出的任何建议!

  1. package main
  2. import "fmt"
  3. type (
  4. User struct {
  5. id int32
  6. firstName string
  7. }
  8. ResultUser struct {
  9. uid int32
  10. fname string
  11. }
  12. SearchResults []ResultUser
  13. )
  14. func main() {
  15. results := getSearchResults()
  16. users := mapResultsToUsers(results) // <-- 这是问题所在
  17. fmt.Println("User struct:", users[0].id, users[0].firstName)
  18. fmt.Println("User struct:", users[1].id, users[1].firstName)
  19. }
  20. // 模拟使用库进行数据查询
  21. func getSearchResults() (results SearchResults) {
  22. return append(results, ResultUser{1, "John"}, ResultUser{2, "Jane"})
  23. }
  24. // 看起来这样做有点不太对劲
  25. // 是否有更符合惯用方式的方法呢?
  26. func mapResultsToUsers(results SearchResults) (users []User) {
  27. for _, result := range results {
  28. users = append(users, User{result.uid, result.fname})
  29. }
  30. return users
  31. }

我见过结构体字段标签,但不确定是否有更好的方法。

英文:

I'm using a third party Go lang library to query a LDAP database of users. The library returns a SearchResult slice of ResultUser that I need to map to my own User struct. The two structs have different field names and I only need specific fields from the ResultUser. Is there a more idiomatic way in Go to transform one struct to another.

I've created a demo below (link also on Go Playground). Thanks in advance for any advice you can give to this Go newbie!

  1. package main
  2. import &quot;fmt&quot;
  3. type (
  4. User struct {
  5. id int32
  6. firstName string
  7. }
  8. ResultUser struct {
  9. uid int32
  10. fname string
  11. }
  12. SearchResults []ResultUser
  13. )
  14. func main() {
  15. results := getSearchResults()
  16. users := mapResultsToUsers(results) // &lt;-- This is the problem
  17. fmt.Println(&quot;User struct:&quot;, users[0].id, users[0].firstName)
  18. fmt.Println(&quot;User struct:&quot;, users[1].id, users[1].firstName)
  19. }
  20. // Simulates a query to a data with a library
  21. func getSearchResults() (results SearchResults) {
  22. return append(results, ResultUser{1, &quot;John&quot;}, ResultUser{2, &quot;Jane&quot;})
  23. }
  24. // Seems like a code smell to have to do this
  25. // Is there a more idiomatic way to do this?
  26. func mapResultsToUsers(results SearchResults) (users []User) {
  27. for _, result := range results {
  28. users = append(users, User{result.uid, result.fname})
  29. }
  30. return users
  31. }

I've seen struct field tags but not sure if there is a better way.

答案1

得分: 1

我认为你已经得到了相当不错的解决方案,尽管我会将映射移到一个专用函数中,类似于:

  1. func fromResultUser(r *ResultUser) *User {
  2. return &User{
  3. id: r.uid,
  4. firstName: r.fname,
  5. }
  6. }

然后mapResultsToUsers变成:

  1. func mapResultsToUsers(results SearchResults) (users []*User) {
  2. for _, result := range results {
  3. users = append(users, fromResultUser(result))
  4. }
  5. return users
  6. }

我见过结构体字段标签,但不确定是否有更好的方法。

你可以组合一些东西,以便可以像这样注释你的User结构体:

  1. User struct {
  2. id int32 `mappedFrom:"uid"`
  3. firstName string `mappedFrom:"fname"`
  4. }

但是,实现这种方法所需的方法比这里介绍的fromResultUser要复杂得多,并且需要熟悉reflect包。我会认为,正如我的一位同事喜欢说的那样,"得不偿失"。

英文:

I think that what you've got is pretty much the best solution, although I would move the mapping into a dedicated function, some like:

  1. func fromResultUser(r *ResultUser) *User {
  2. return &amp;User{
  3. id: r.uid,
  4. firstName: r.fname,
  5. }
  6. }

Then mapResultsToUsers becomes:

  1. func mapResultsToUsers(results SearchResults) (users []*User) {
  2. for _, result := range results {
  3. users = append(users, fromResultUser(result))
  4. }
  5. return users
  6. }

> I've seen struct field tags but not sure if there is a better way.

You could put together something so that you could annotate your User struct like:

  1. User struct {
  2. id int32 `mappedFrom:&quot;uid&quot;`
  3. firstName string `mappedFrom:&quot;fname&quot;`
  4. }

But the method required to implement that would be substantially more complex than the fromResultUser presented here, and would involve becoming familiar with the reflect package. I would argue that, as a colleague of mine is fond of saying, "the juice isn't worth the squeeze".

huangapple
  • 本文由 发表于 2023年2月24日 09:46:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/75551930.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定