【问题标题】:Merge structs with some overlapping fields合并具有一些重叠字段的结构
【发布时间】:2020-07-26 20:54:11
【问题描述】:

我看到几个问题询问如何合并唯一的结构以及如何合并相同的结构。

但是我将如何合并有一些重叠的结构呢?哪些字段被占用以及何时被占用?

例如:

    type structOne struct {
        id string `json:id`
        date string `json:date`
        desc string `json:desc`
    }

    type structTwo struct {
        id string `json:id`
        date string `json:date`
        name string `json:name`
    }

我将如何合并它以便我得到

{
    id string `json:id`
    date string `json:date`
    desc string `json:desc`
    name string `json:name`
}

另外,如果在这种情况下两个 id 相同(假设通过 id 连接)但名称不同,会发生什么情况? 在 javascript 中,执行类似Object.assign(structOne, structTwo) 的操作。

【问题讨论】:

  • 这真的取决于你如何“合并”两个结构。鉴于有无数种方法可以从其他两个结构生成新结构,我们更容易回答如何实现您想要的行为,或者解释指定“合并”方式的行为。

标签: go


【解决方案1】:

Go 是一种强类型语言,与 javascript 不同,您不能将两个结构合并为一个组合结构,因为所有类型都是在编译时确定的。你有两个解决方案:

使用嵌入式结构:

一个很好的解决方案是使用嵌入式结构,因为您不必再​​合并任何东西了。

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "log"
)

// Shared field
type common struct {
    ID   string `json:id`
    Date string `json:date`
}

type merged struct {
    // Common field is embedded
    common

    Name string `json:name`
    Desc string `json:desc`
}

func main() {
    buf := bytes.Buffer{}
    buf.WriteString("{ \"id\": \"1\",   \"date\": \"27/07/2020\", \"desc\": \"the decription...\"   }")

    merged := &merged{}

    err := json.Unmarshal(buf.Bytes(), merged)
    if err != nil {
        log.Fatal(err)
    }

    // Look how you can easily access field from
    // embedded struct
    fmt.Println("ID:", merged.ID)
    fmt.Println("Date:", merged.Date)
    fmt.Println("Name:", merged.Name)
    fmt.Println("Desc:", merged.Desc)

    // Output:
    // ID: 1
    // Date: 27/07/2020
    // Name:
    // Desc: the decription...
}

如果您想了解更多关于结构嵌入的信息: golangbyexample.com travix.io

使用地图

另一种解决方案是使用映射,但您将失去结构和方法的好处。这个例子不是最简单的,但在其他回复中有一些很好的例子。

在这个例子中,我使用Mergo。 Mergo 是一个可以合并结构和映射的库。这里它用于在 Map 方法中创建地图对象,但您完全可以编写自己的方法。

package main

import (
    "fmt"
    "log"

    "github.com/imdario/mergo"
)

type tOne struct {
    ID   string
    Date string
    Desc string
}

// Map build a map object from the struct tOne
func (t1 tOne) Map() map[string]interface{} {
    m := make(map[string]interface{}, 3)
    if err := mergo.Map(&m, t1); err != nil {
        log.Fatal(err)
    }

    return m
}

type tTwo struct {
    ID   string
    Date string
    Name string
}

// Map build a map object from the struct tTwo
func (t2 tTwo) Map() map[string]interface{} {
    m := make(map[string]interface{}, 3)
    if err := mergo.Map(&m, t2); err != nil {
        log.Fatal(err)
    }

    return m
}

func main() {
    dst := tOne{
        ID:   "destination",
        Date: "26/07/2020",
        Desc: "destination object",
    }.Map()

    src := tTwo{
        ID:   "src",
        Date: "26/07/1010",
        Name: "source name",
    }.Map()

    if err := mergo.Merge(&dst, src); err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Destination:\n%+v", dst)
    // Output:
    // Destination:
    // map[date:26/07/2020 desc:destination object iD:destination name:object name
}

【讨论】:

  • 感谢您的解释和建议!我采用了使用嵌入式结构的方法
  • 类型并非全部在编译时确定,因为在运行时您可以使用reflect.StructOf创建新的结构类型,并创建这种新类型的值。
【解决方案2】:

Go 结构和 JavaScript 对象非常不同。 Go 结构没有动态字段。

如果您想要可以轻松迭代和合并并且对 JSON 非常友好的动态键/值集,为什么不使用 map[string]interface{}

$ go run t.go
map[a:1 b:4]
map[a:1 b:4 c:3]
$ cat t.go
package main

import(
  "fmt"
)

type MyObj map[string]interface{}

func (mo MyObj)Merge(omo MyObj){
  for k, v := range omo {
   mo[k] = v
  }
}

func main() {
  a := MyObj{"a": 1, "b": 4}
  b := MyObj{"b": 2, "c": 3}
  b.Merge(a)
  fmt.Printf("%+v\n%+v\n", a, b)
}

【讨论】:

    【解决方案3】:

    您可以使用github.com/fatih/structs 将您的结构转换为映射。然后遍历该地图并选择需要复制的字段。我有一段代码说明了这个解决方案。

    func MergeStruct (a structOne,b structTwo) map[string]interface{}{
       a1:=structs.Map(a)
       b1:=structs.Map(b)
      /* values of structTwo over writes values of structOne */
       var myMap=make(map[string]interface{})
         for val,key:=range(a1){
            myMap[key]=val
         }
         for val,key:=range(b1){
            myMap[key]=val
         }
     return myMap
    }
    

    【讨论】:

      【解决方案4】:

      您可以使用reflect 包来执行此操作。尝试遍历这两个结构,然后您可以使用另一个 struct 类型来存储值,或者使用 map

      查看this 问题,了解如何迭代struct。 查看this 问题,了解如何获取字段名称。

      请记住使用导出的字段名称以使 reflect 包正常工作。

      Here 是一个有效的例子。

      【讨论】:

        猜你喜欢
        • 2021-12-06
        • 1970-01-01
        • 1970-01-01
        • 2016-07-25
        • 2023-03-12
        • 1970-01-01
        • 1970-01-01
        • 2014-12-11
        • 2023-03-23
        相关资源
        最近更新 更多