Copy one struct to another where structs have same members and different types

Go

Go Problem Overview


I have two struct having the same members, I want to copy one struct to another, see the pseudo code below:

type Common struct {
    Gender int
    From   string
    To     string
}

type Foo struct {
    Id    string
    Name  string
    Extra Common
}

type Bar struct {
    Id    string
    Name  string
    Extra Common
}

Then I have foo of struct Foo, and bar of struct Bar, Is there any way to copy bar from foo?

Go Solutions


Solution 1 - Go

Use a conversion to change the type. The following code uses a conversion to copy a value of type Foo to a value of type Bar:

foo := Foo{Id: "123", Name: "Joe"}
bar := Bar(foo)

playground example

The conversion only works when the underlying types are identical except for struct tags.

Solution 2 - Go

https://github.com/jinzhu/copier (same author of gorm) is also quite a good one, I have nested structs and all I do is:

copier.Copy(&employees, &user)

works great

Solution 3 - Go

If you would like to copy or clone to a different struct, I would suggest using deepcopier

It provides nice features like skipping, custom mapping, and forcing. below is an example from github:

Install:

go get -u github.com/ulule/deepcopier

Example:

package main

import (
    "fmt"
 
    "github.com/ulule/deepcopier"
)

// Model
type User struct {
    // Basic string field
    Name  string
    // Deepcopier supports https://golang.org/pkg/database/sql/driver/#Valuer
    Email sql.NullString
}

func (u *User) MethodThatTakesContext(ctx map[string]interface{}) string {
    // do whatever you want
    return "hello from this method"
}

// Resource
type UserResource struct {
    //copy from field "Name"
    DisplayName            string `deepcopier:"field:Name"`
    //this will be skipped in copy 
    SkipMe                 string `deepcopier:"skip"`
    //this should call method named MethodThatTakesContext 
    MethodThatTakesContext string `deepcopier:"context"`
    Email                  string `deepcopier:"force"`

}

func main() {
    user := &User{
        Name: "gilles",
        Email: sql.NullString{
            Valid: true,
            String: "[email protected]",
        },
    }

    resource := &UserResource{}

    deepcopier.Copy(user).To(resource)
    //copied from User's Name field
    fmt.Println(resource.DisplayName)//output: gilles
    fmt.Println(resource.Email) //output: [email protected]
    fmt.Println(resource.MethodThatTakesContext) //output: hello from this method
}

Also, some other way you could achieve this is by encoding the source object to JSON and then decode it back to the destination object.

Solution 4 - Go

This is another possible answer

type Common struct { Gender int From string To string }

type Foo struct {
	Id    string
	Name  string
	Extra Common
}

type Bar struct {
	Id    string
	Name  string
	Extra Common
}
foo:=Foo{
	Id:"123",
	Name:"damitha",
	Extra: struct {
		Gender int
		From   string
		To     string
	}{Gender:1 , From:"xx", To:"yy" },
}
bar:=*(*Bar)(unsafe.Pointer(&foo))
fmt.Printf("%+v\n",bar)

Solution 5 - Go

If you would like to copy or clone to a different struct, I would suggest using deepcopier

It provides nice features like skipping, custom mapping, and forcing.

You can achieve nested struct copy following way. Install:

go get -u github.com/ulule/deepcopier

Example:

 package main
    
    import (
    	"fmt"
    	"strconv"
    
    	"github.com/ulule/deepcopier"
    )
    
    //FieldStruct -  Field Struct
    type FieldStruct struct {
    	Name string `deepcopier:"field:TargetName"`
    	Type string `deepcopier:"field:TargetType"`
    }
    
    //SourceStruct - Source Struct
    type SourceStruct struct {
    	Name        string   `deepcopier:"field:TargetName"`
    	Age         int      `deepcopier:"field:TargetAge"`
    	StringArray []string `deepcopier:"field:TargetStringArray"`
    	StringToInt string   `deepcopier:"context"`
    	Field       FieldStruct
    	Fields      []FieldStruct
    }
    
    //TargetFieldStruct -  Field Struct
    type TargetFieldStruct struct {
    	TargetName string
    	TargetType string
    }
    
    //TargetStruct - Target Struct
    type TargetStruct struct {
    	TargetName        string
    	TargetAge         int
    	TargetStringArray []string
    	TargetInt         int
    	TargetField       TargetFieldStruct
    	TargetFields      []TargetFieldStruct
    }
    
    //write methods
    
    //TargetInt - StringToInt
    func (s *SourceStruct) TargetInt() int {
    	i, _ := strconv.Atoi(s.StringToInt)
    	return i
    }
    
    func main() {
    	s := &SourceStruct{
    		Name:        "Name",
    		Age:         12,
    		StringArray: []string{"1", "2"},
    		StringToInt: "123",
    		Field: FieldStruct{
    			Name: "Field",
    			Type: "String",
    		},
    		Fields: []FieldStruct{
    			FieldStruct{
    				Name: "Field1",
    				Type: "String1",
    			},
    			FieldStruct{
    				Name: "Field2",
    				Type: "String2",
    			},
    		},
    	}
    
    	t := &TargetStruct{}
    
    	//coping data into inner struct
    	deepcopier.Copy(&t.TargetField).From(&s.Field)
    
    	// copied array of Struct
    	for i := range s.Fields {
    		// init a struct
    		t.TargetFields = append(t.TargetFields, TargetFieldStruct{})
    		// coping the data
    		deepcopier.Copy(&t.TargetFields[i]).From(&s.Fields[i])
    	}
    	//Top level copy
    	deepcopier.Copy(t).From(s)
    
    	fmt.Println(t)
    }

Output: &{Name 12 [1 2] 123 {Field String} [{Field1 String1} {Field2 String2}]}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjsvisaView Question on Stackoverflow
Solution 1 - GoBayta DarellView Answer on Stackoverflow
Solution 2 - Gomel3kingsView Answer on Stackoverflow
Solution 3 - GoMuhammad SolimanView Answer on Stackoverflow
Solution 4 - GoDamitha DayanandaView Answer on Stackoverflow
Solution 5 - GopilathrajView Answer on Stackoverflow