如何在Golang中合并两个结构体?

28

我有两个可以转换为JSON的匿名结构体。

a := struct {
    Name string `json:"name"`
}{"my name"}

b := struct {
    Description string `json:"description"`
}{"my description"}

有没有办法将它们合并成json以获得像这样的东西:

{
    "name":"my name",
    "description":"my description"
}

fmt.Sprintf("{\"name\":%q,\"description\":%q}", a.Name, b.Description) 有什么问题吗?有些事情确实需要一些编程... - Volker
5个回答

33

你可以在另一个结构体中嵌入这两个结构体

type name struct {
    Name string `json:"name"`
}

type description struct {
    Description string `json:"description"`
}

type combined struct {
    name
    description
}

JSON包将类似于联合体的方式处理嵌入式结构体,但这很快就会变得笨拙。


细微之处在于该结构体是匿名的。我不知道它的名称。因此,我无法使用嵌入。 - Dmitry Kapsamun
1
@DmitryKapsamun:你不能创建一个类型吗?为什么结构体是匿名的? - Elias Van Ootegem
优雅,我喜欢GO语言中这种操作的简洁性。 - Yagiz Degirmenci

20

这有点绕,但我想你可以按照以下方式操作:

    a := struct {
        Name string `json:"name"`
    }{"my name"}

    b := struct {
        Description string `json:"description"`
    }{"my description"}

    var m map[string]string

    ja, _ := json.Marshal(a)
    json.Unmarshal(ja, &m)
    jb, _ := json.Marshal(b)
    json.Unmarshal(jb, &m)

    jm, _ := json.Marshal(m)
    fmt.Println(string(jm))

这正是我所需要的!谢谢! - Dmitry Kapsamun
如果你的结构体中有 omitempty 的 json 标签,可能会出现问题。如果是这样,空值将不会被擦除。 - Dmytro Boichenko

8

Go强调组合而非继承。不幸的是,您正在使用匿名结构体,但鉴于您显然正在尝试进行json编组,最好将它们定义为类型:

type name struct {
    Name string `json:"name"`
}
type desc struct {
    Description string `json:"description"`
}

您可以像当前一样初始化它们,但是不再使用struct{<fields>}{init}这样的方式,而是直接写。
a := name{"foo"}
b := desc{"Description"}

您可以通过编写以下内容将它们以任何方式组合起来:
c := struct {
    name
    description
}{a, b}

在编写此类类型时,您需要习惯的一个小技巧(可能会让您一开始有些困惑)是如何初始化成员。假设您决定创建一个组合两个其他结构体的类型:

type foo struct {
    name
    description
}

您不能像这样初始化它:
o := foo{"Name value", "description value"}

Go会抱怨你将类型字符串用作类型name。你需要编写像这样的代码:

o := foo{
    name{"Name value"},
    description{Description: "Description val"},//optional with field names
}

使用现有对象构建的内联复合体(例如 c := struct{}{a, b})已经实现了这一目的。
根据您要做什么,有时编写类似于以下内容会更容易些:
func (s *MyCompositeType) CopyName(n name) {
    s.Name = n.Name
    //copy other fields
}

当您嵌套复合类型多层深度时,它可以使生活更加轻松。


如果你将 o 转换为 json,那么这个字符串会是什么样子? - tutley
@tutley:在Go Playground上试一下。它应该看起来像这样:{"name": "名称值","description": "描述值"}。其他类型未导出,嵌入结构体。 - Elias Van Ootegem

6
您可以像这样合并两个结构体:

您可以像这样合并两个结构体:

package main

import (
    "fmt"
    "encoding/json"
)


type b struct {
    Name string  `json:"name"`
    Description string
    Url string
}

type a struct {
    *b
    MimeType string  `json:"mimeType"`
}

func main() {
    bc := b{"test", "testdecription", "testurl"}
    ac := a{nil, "jpg"}

    ac.b = &bc

    js, _ := json.Marshal(ac)

    fmt.Println(string(js) )
}

3

其中一种方法是使用内置的reflect包并通过编程方式创建一个新的结构体!

func stringInSlice(value string, slice []string) bool {
    for _, elem := range slice {
        if elem == value {
            return true
        }
    }
    return false
}

func mergeStructs(structs ...interface{}) reflect.Type {
    var structFields []reflect.StructField
    var structFieldNames []string

    for _, item := range structs {
        rt := reflect.TypeOf(item)
        for i := 0; i < rt.NumField(); i++ {
            field := rt.Field(i)
            if !stringInSlice(field.Name, structFieldNames) {
                structFields = append(structFields, field)
                structFieldNames = append(structFieldNames, field.Name)
            }
        }
    }

    return reflect.StructOf(structFields)
}

Go Playground: Link


如何将 reflect.Type 转换为结构体类型? - Anish Jain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接