Golang:不同结构类型之间的转换是可能的吗?

33qvvth1  于 2023-08-01  发布在  Go
关注(0)|答案(8)|浏览(112)

假设我有两个类似的类型,以这种方式设置:

type type1 []struct {
    Field1 string
    Field2 int
}
type type2 []struct {
    Field1 string
    Field2 int
}

字符串
有没有一种直接的方法可以将type1的值写入type2,并且知道它们具有相同的字段?(除了编写一个循环将所有字段从源复制到目标)
谢谢

x6yk4ghg

x6yk4ghg1#

要给予OneOfOne的答案,请参阅规范的转换部分。
它指出
在以下任何情况下,非常量值x都可以转换为T类型:

  • x可分配给T
    *x的类型与T的底层类型相同。
  • x的类型和T是未命名的指针类型,它们的指针基类型具有相同的基础类型。
  • x的类型和T都是整数或浮点类型。
  • x的类型和T都是复杂类型。
  • x是一个整数,或者是字节或符文的片段,T是字符串类型。
  • x是一个字符串,T是一个字节或符文片。

第一个突出显示的案例是您的案例。这两种类型都有基础类型

[]struct { Field1 string Field2 int }

字符串
基础类型定义为
如果T是预先声明的布尔、数字或字符串类型之一,或者是类型文字,则对应的基础类型是T本身。否则,T的基础类型是T在其类型声明中引用的类型的基础类型。(规格、类型)
您正在使用类型文字来定义类型,因此此类型文字是您的基础类型。

pw136qt2

pw136qt22#

对于您的特定示例,您可以轻松地将其转换为playground

t1 := type1{{"A", 1}, {"B", 2}}
t2 := type2(t1)
fmt.Println(t2)

字符串

yebdmbv4

yebdmbv43#

从Go 1.8开始,在将值从一种结构类型转换为另一种类型时,结构标记被忽略。类型type1和type2将是可转换的,不管它们的struct标签如何,在那个Go版本中。https://beta.golang.org/doc/go1.8#language

fdbelqdn

fdbelqdn4#

Nicolas,在你后来的评论中你说你在结构体上使用了字段标签;这些都是定义的一部分,所以下面定义的t1和t2是不同的,你不能转换t2(t1):

type t1 struct {
    Field1 string
}

type t2 struct {
    Field1 string `json:"field_1"`
}

字符串

更新:从Go 1.8开始不再如此

sdnqo3pr

sdnqo3pr5#

这不是标准的方法,但是如果你希望有一个灵活的方法来将结构体转换为,比如说,Map,或者如果你想摆脱结构体的一些属性而不使用`json:“-",你可以使用JSON marshal
具体来说,我是这样做的:

type originalStruct []struct {
    Field1 string
    Field2 int
}

targetStruct := make(map[string]interface{}) // `targetStruct` can be anything of your choice

temporaryVariable, _ := json.Marshal(originalStruct)
err = json.Unmarshal(temporaryVariable, &targetStruct) 
if err != nil {
    // Catch the exception to handle it as per your need
}

字符串
可能看起来像一个黑客,但在我的大多数任务中非常有用。

blmhpbnm

blmhpbnm6#

对于已经支持泛型的go v1.18,基本上我只需要创建一个方法来接受任何类型的参数,并使用json将其转换为另一种类型。

// utils.TypeConverter
func TypeConverter[R any](data any) (*R, error) {
    var result R
    b, err := json.Marshal(&data)
    if err != nil {
      return nil, err
    }
    err = json.Unmarshal(b, &result)
    if err != nil {
      return nil, err
    }
    return &result, err
}

字符串
假设我有一个名为models.CreateUserRequest的结构体,我想将它转换为models.User。注意json标签必须相同

// models.CreateUserRequest
type CreateUserRequest struct {
   Fullname         string `json:"name,omitempty"`
   RegisterEmail    string `json:"email,omitempty"`
}

// models.User
type User struct {
   Name     string `json:"name,omitempty"`
   Email    string `json:"email,omitempty"`
   Phone    string `json:"phone,omitempty"`
}


我可以像这样使用上面的utils方法

user := models.CreateUserRequest {
    Name: "John Doe",
    Email: "johndoe@gmail.com"
}
data, err := utils.TypeConverter[models.User](&user)
if err != nil {
    log.Println(err.Error())
}
log.Println(reflrect.TypeOf(data)) // will output *models.User
log.Println(data)

gwo2fgha

gwo2fgha7#

您可以手动使用Map器函数,将类型为t1的每个元素Map到类型为t2的元素。会成功的

func GetT2FromT1(ob1 *t1) *t2 {
     ob2 := &t2 { Field1: t1.Field1, }
     return ob2
}

字符串

vdgimpew

vdgimpew8#

如果您可以手动编写这些转换,Agniswar Bakshi's answer会更快更好,但这里是Furqan Rahamath's answer的扩展。(更完整的示例可参见on the Golang playground

func Recast(a, b interface{}) error {
    js, err := json.Marshal(a)
    if err != nil {
        return err
    }
    return json.Unmarshal(js, b)
}

// Usage:

type User struct {
    Name string
    PasswordHash string
}

// remove PasswordHash before providing user:
type PrivateOutgoingUser struct {
    Name string
}

u1 := &User{Name: "Alice", PasswordHash: "argon2...."}
u2 := &PrivateOutgoingUser{}
err = Recast(u1, u2)
if err != nil {
    log.Panic("Error recasting u1 to u2", err)
}
log.Println("Limited user:", u2)

字符串
这里有另一种使用JSON标记的方法,它更快,因为它不需要额外的marshal-unmarshal步骤,但不太灵活:

type User struct {
    Name string
    PasswordHash string `json:"-"` // - removes the field with JSON
}

user := &User{Name: "Tommy Tester", PasswordHash: "argon2...."}
js, err := json.Marshal(user)
log.Println("Limited user:", string(user))

相关问题