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

Nic*_*all 46 struct go

假设我有两种类似的类型:

type type1 []struct {
    Field1 string
    Field2 int
}
type type2 []struct {
    Field1 string
    Field2 int
}
Run Code Online (Sandbox Code Playgroud)

是否有直接的方法将值从type1写入type2,知道它们具有相同的字段?(除了编写一个循环,将所有字段从源复制到目标)

谢谢.

nem*_*emo 50

要提供OneOfOne答案的参考,请参阅规范的" 转换"部分.

它说明了这一点

在任何这些情况下,非常量值x都可以转换为类型T:

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

第一个和突出显示的案例是你的情况.两种类型都有底层类型

[]struct { Field1 string Field2 int }
Run Code Online (Sandbox Code Playgroud)

基础类型定义为

如果T是预先声明的布尔,数字或字符串类型之一或类型文字,则相应的基础类型T本身.否则,T底层类型是T其类型声明中引用的类型的基础类型.(规格,类型)

您正在使用类型文字来定义类型,因此此类型文字是您的基础类型.

  • 请提供更多细节的另一个问题. (2认同)

One*_*One 38

对于您的具体示例,您可以轻松转换它playground:

t1 := type1{{"A", 1}, {"B", 2}}
t2 := type2(t1)
fmt.Println(t2)
Run Code Online (Sandbox Code Playgroud)

  • @Richard:不,如果结构的类型不匹配,它不会恐慌,它只是不会编译. (17认同)
  • 如果第一个结构是由lib提供的,但是您想从第二个结构生成json或xml,则这很有用。 (4认同)
  • 虽然这是可能的,但这有点无稽之谈。本质上,您正在更改类型。如果结构不匹配,则代码抛出异常。 (2认同)

小智 14

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


m.k*_*ski 11

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

type t1 struct {
    Field1 string
}

type t2 struct {
    Field1 string `json:"field_1"`
}
Run Code Online (Sandbox Code Playgroud)

  • 从Go 1.8开始,转换时会忽略标记.https://golang.org/doc/go1.8#language (3认同)

Moh*_*h M 10

这不是标准方法,但是如果您希望有一种灵活的方法将结构转换为映射,或者如果您想在不使用 `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
}
Run Code Online (Sandbox Code Playgroud)

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


小智 7

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

// 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
}
Run Code Online (Sandbox Code Playgroud)

假设我有一个名为 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"`
}
Run Code Online (Sandbox Code Playgroud)

我可以像这样使用上面的 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)
Run Code Online (Sandbox Code Playgroud)


Agn*_*shi 5

您可以手动使用映射器函数将类型 t1 的每个元素映射到类型 t2。它会起作用的。

func GetT2FromT1(ob1 *t1) *t2 {
     ob2 := &t2 { Field1: t1.Field1, }
     return ob2
}
Run Code Online (Sandbox Code Playgroud)