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

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

How can I merge two structs in Golang?

问题

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

a := struct {
	Name string `json:"name"`
}{"我的名字"}

b := struct {
	Description string `json:"description"`
}{"我的描述"}

有没有办法将它们合并成JSON,得到像这样的结果:

{
	"name":"我的名字",
	"description":"我的描述"
}
英文:

I have two json-marshallable anonymous structs.

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

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

Is there any way to merge them into json to get something like that:

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

答案1

得分: 32

你可以将两个结构体嵌入到另一个结构体中

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

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

type combined struct {
	name
	description
}

JSON包会将嵌入的结构体视为联合体,但这可能会变得很笨重。

英文:

You can embed both structs in another.

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

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

type combined struct {
	name
	description
}

The JSON package will treat embedded structs kind of like unions, but this can get clunky pretty quickly.

答案2

得分: 18

这是一个有点复杂的例子,但我想你可以这样做:

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))

这段代码的作用是将结构体 ab 转换为 JSON 字符串,然后将它们解析为一个 map[string]string 类型的变量 m。最后,将 m 转换为 JSON 字符串并打印出来。

英文:

It's a bit convoluted but I suppose you could do something like this:

    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))

答案3

得分: 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
    desc
}{a, b}

一个需要注意的怪癖(可能会让你一开始感到困惑)是如何初始化成员。假设你决定创建一个结合了其他两个结构体的类型:

type foo struct {
    name
    desc
}

你不能这样初始化它:

o := foo{"Name value", "description value"}

Go会抱怨你将类型string用作类型name。你需要这样写:

o := foo{
    name{"Name value"},
    desc{Description: "Description val"}, // 可选,使用字段名
}

使用现有对象构建的内联组合(类似于c := struct{}{a, b})已经实现了这一点。

根据你想要做什么,有时候编写类似下面的代码会更容易:

func (s *MyCompositeType) CopyName(n name) {
    s.Name = n.Name
    // 复制其他字段
}

当你嵌套多层复合类型时,这样做会让生活更轻松。

英文:

Go is all about composition over inheritance. Sadly, you're using anonymous structs, but given that you're clearly trying to json marshal them, you're better of defining them as types:

type name struct {
    Name string `json:&quot;name&quot;`
}
type desc struct {
    Description string `json:&quot;description&quot;`
}

You can initialize them in the same way as you're currently doing, but instead of struct{&lt;fields&gt;}{init}, you'll just write

a := name{&quot;foo&quot;}
b := desc{&quot;Description&quot;}

You can then combine them in any way you like by writing:

c := struct {
    name
    description
}{a, b}

One quirk (that might trip you up at first) you have to get used to when composing types like this is how you initialize the members. Say you decide to create a type that combines the other two structs:

type foo struct {
    name
    description
}

You cannot initialize it like this:

o := foo{&quot;Name value&quot;, &quot;description value&quot;}

Go will complain about you using type string as type name. You'll have to write something like this:

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

An in-line composite built using existing objects (cf c := struct{}{a, b}) does this already.<br>
Depending on what you're trying to do, it sometimes is easier to write something like this:

func (s *MyCompositeType) CopyName(n name) {
    s.Name = n.Name
    //copy other fields
}

It makes life easier when you're nesting composite types several levels deep.

答案4

得分: 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))
}

这段代码将两个结构体ab合并,并使用json.Marshal将其转换为JSON字符串。

英文:

You can merge two struct like this :

package main

import (
	&quot;fmt&quot;
	&quot;encoding/json&quot;
)


type b struct {
	Name string  `json:&quot;name&quot;`
	Description string
	Url string
}

type a struct {
    *b
    MimeType string  `json:&quot;mimeType&quot;`
}

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

	ac.b = &amp;bc
	
	js, _ := json.Marshal(ac)

	fmt.Println(string(js) )
}

答案5

得分: 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: [链接][1]
英文:

One approach to this is to use the inbuilt reflect package and create a new struct programmatically!

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 &lt; 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

huangapple
  • 本文由 发表于 2016年11月9日 22:40:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/40509575.html
匿名

发表评论

匿名网友

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

确定