将一个结构体赋值给另一个结构体。

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

Assign struct with another struct

问题

我有一个RegistrationRequest结构体:

type RegistrationRequest struct {
    Email    *string
    Email2   *string        
    Username *string
    Password *string
    Name     string
}

其中Email2是再次输入的电子邮件值,用于验证用户输入的是否正确。

我还有一个User结构体:

type User struct {
    Email    *string
    Username *string
    Password *string
    Name     string           
}

当然,在注册之后没有必要存储Email2

所以我有两个变量:requ - 分别对应这两个结构体。是否可以将req结构体赋值给u结构体,以便u结构体中存在所有共同的字段?

英文:

I have a RegistrationRequest struct:

type RegistrationRequest struct {
    Email    *string
    Email2   *string        
    Username *string
    Password *string
    Name     string
}

Where Email2 is the email value entered again to verify that what the user entered is correct.

I also have a User struct:

type User struct {
    Email    *string
    Username *string
    Password *string
    Name     string           
}

Of course, there is no need to store Email2 beyond registration.

So I have two variables: req and u - one for each struct. Is it possible to assign the req struct into to the u struct so that all the common fields will exist in the u struct?

答案1

得分: 33

使用简单的赋值语句是无法实现的,因为尽管User的字段是RegistrationRequest的子集,但它们是完全不同的类型,不适用可赋值性规则

你可以编写一个使用反射(reflect包)的函数,将req的所有字段复制到u,但这样做很丑陋(而且效率低下)。

最好的方法是重构你的类型,并且RegistrationRequest可以嵌入User

这样做的话,如果你有一个RegistrationRequest类型的值,那么你已经有了一个User类型的值:

type User struct {
    Email    *string
    Username *string
    Password *string
    Name     string
}

type RegistrationRequest struct {
    User  // 嵌入User类型
    Email2 *string
}

func main() {
    req := RegistrationRequest{}
    s := "as@as.com"
    req.Email = &s

    s2 := "testuser"
    req.Username = &s2

    u := User{}
    u = req.User
    fmt.Println(*u.Username, *u.Email)
}

输出结果:(在Go Playground上尝试)

testuser as@as.com

请注意,由于你的结构体包含指针,当复制一个结构体时,指针值将被复制,而不是指向的值。我不确定为什么你在这里需要指针,最好将所有字段声明为非指针。

还请注意,嵌入并不是真正的要求,它只是使你的类型及其使用更加顺畅。User也可以作为RequistrationRequest的一个普通字段,例如:

type RegistrationRequest struct {
    Usr User // 这只是一个普通字段,不是嵌入
    Email2 *string
}
英文:

Using simple assignment you can't because even though the fields of User are a subset of RegistrationRequest, they are completely 2 different types, and Assignability rules don't apply.

You could write a function which uses reflection (reflect package), and would copy all the fields from req to u, but that is just ugly (and inefficient).

Best would be to refactor your types, and RegistrationRequest could embed User.

Doing so if you have a value of type RegistrationRequest that means you already also have a value of User:

type User struct {
	Email    *string
	Username *string
	Password *string
	Name     string
}

type RegistrationRequest struct {
	User  // Embedding User type
	Email2 *string
}

func main() {
	req := RegistrationRequest{}
	s := "as@as.com"
	req.Email = &s

	s2 := "testuser"
	req.Username = &s2

	u := User{}
	u = req.User
	fmt.Println(*u.Username, *u.Email)
}

Output: (try it on the Go Playground)

testuser as@as.com

Also please note that since your structs contain pointers, when copying a struct, pointer values will be copied and not pointed values. I'm not sure why you need pointers here, would be best to just declare all fields to be non-pointers.

Also note that embedding is not really a requirement, it just makes your types and their usage more smooth. User could just as well be an "ordinary" field of RequistrationRequest, e.g.:

type RegistrationRequest struct {
    Usr User // This is just an ordinary field, not embedding
    Email2 *string
}

答案2

得分: 5

你可以使用"github.com/jinzhu/copier"包来在包含相同字段名称的结构体之间进行复制。该包使用反射来实现这一功能。

package main

import (
	"fmt"
	"github.com/jinzhu/copier"
)

type RegistrationRequest struct {
	Email    *string
	Email2   *string
	Username *string
	Password *string
	Name     string
}

type User struct {
	Email    *string
	Username *string
	Password *string
	Name     string
}

func main() {
	user := new(User)
	req := new(RegistrationRequest)
	user.Email, user.Password, user.Username = new(string), new(string), new(string)
	user.Name = "John Doe"
	*user.Email = "a@b.com"
	*user.Password = "1234"
	*user.Username = "johndoe"
	fmt.Println("User :", user.Name, *user.Email, *user.Username, *user.Password)
	copier.Copy(req, user)
	fmt.Println("RegistrationRequest :", req.Name, *req.Email, *req.Username, *req.Password)
}

输出结果:

User : John Doe a@b.com johndoe 1234
RegistrationRequest : John Doe a@b.com johndoe 1234
英文:

You can use "github.com/jinzhu/copier" package to Copy between structs containing same field name. This package uses reflection to do this.

package main

import (
	"fmt"  
	"github.com/jinzhu/copier"
)

type RegistrationRequest struct {
	Email    *string
	Email2   *string
	Username *string
	Password *string
	Name     string
}

type User struct {
	Email    *string
	Username *string
	Password *string
	Name     string
}

func main() {
	user := new(User)
	req := new(RegistrationRequest)
	user.Email, user.Password, user.Username = new(string), new(string), new(string)
	user.Name = "John Doe"
	*user.Email = "a@b.com"
	*user.Password = "1234"
	*user.Username = "johndoe"
	fmt.Println("User :",user.Name, *user.Email, *user.Username, *user.Password)
	copier.Copy(req, user)
	fmt.Println("RegistrationRequest :",req.Name, *req.Email, *req.Username, *req.Password)
}

Output

 User : John Doe a@b.com johndoe 1234
 RegistrationRequest : John Doe a@b.com johndoe 1234

答案3

得分: 0

func ObjectAssign(target interface{}, object interface{}) {
// 将 object 的属性值赋给 target 的属性值
// 使用模式匹配(https://golang.org/pkg/reflect/#Value.FieldByName)
// 参考:https://stackoverflow.com/questions/35590190/how-to-use-the-spread-operator-in-golang
t := reflect.ValueOf(target).Elem()
o := reflect.ValueOf(object).Elem()
for i := 0; i < o.NumField(); i++ {
for j := 0; j < t.NumField(); j++ {
if t.Field(j).Name() == o.Field(i).Name() {
t.Field(j).Set(o.Field(i))
}
}
}
}

英文:
func ObjectAssign(target interface{}, object interface{}) {
    // object atributes values in target atributes values
    // using pattern matching (https://golang.org/pkg/reflect/#Value.FieldByName)
    // https://stackoverflow.com/questions/35590190/how-to-use-the-spread-operator-in-golang
    t := reflect.ValueOf(target).Elem()
    o := reflect.ValueOf(object).Elem()
    for i := 0; i &lt; o.NumField(); i++ {
        for j := 0; j &lt; t.NumField(); j++ {
            if t.Field(j).Name() == o.Field(i).Name() {
                t.Field(j).Set(o.Field(i))
            }
        }
    }
}

答案4

得分: 0

如果第二个结构体是第一个结构体的克隆,并且字段较少,你可以通过json来转换结构体。

type user1 struct {
    FirstName string `json:"first_name"`
    LastName  string `json:"last_name"`
    UserName  string `json:"user_name"`
}

type user2 struct {
    FirstName string `json:"first_name"`
    LastName  string `json:"last_name"`
}
...

u1Json, _ := json.Marshal(u1)
_ = json.Unmarshal(u1Json, &u2)

以上是代码的翻译部分。

英文:

If the second structure is a clone of the first one with fewer fields you can convert the structures via json.

type user1 struct {
 FirstName string `json:&quot;first_name&quot;`
 LastName string `json:&quot;last_name&quot;`
 UserName string `json:&quot;user_name&quot;` 
}

type user2 struct {
 FirstName string `json:&quot;first_name&quot;`
 LastName string `json:&quot;last_name&quot;`
}

...

u1Json, _ := json.Marshal(u1)
_ = json.Unmarshal(u1Json,&amp;u2)

huangapple
  • 本文由 发表于 2015年8月13日 15:03:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/31981592.html
匿名

发表评论

匿名网友

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

确定