How to initialize inherited object's fields - go

I must be missing something. I cannot initialize an object's inherited fields without directly accessing them.
My goal is trying to keep it simple.
package main
type Page struct {
Title string
}
type Article struct {
Page
Id int
}
func main() {
// this generates a build error:
// "invalid field name Title in struct initializer"
//
p := &Article{
Title: "Welcome!",
Id: 2,
}
// this generates a build error:
// "invalid field name Page.Title in struct initializer"
//
p := &Article{
Page.Title: "Welcome!",
Id: 2,
}
// this works, but is verbose... trying to avoid this
//
p := &Article{
Id: 2,
}
p.Title = "Welcome!"
// as well as this, since the above was just a shortcut
//
p := &Article{
Id: 2,
}
p.Page.Title = "Welcome!"
}
Thanks in advance.

In Go, these fields from embedded structs are called promoted fields.
The Go Specification states (my emphasis):
Promoted fields act like ordinary fields of a struct except that they cannot be used as field names in composite literals of the struct.
This is how you can solve it:
p := &Article{
Page: Page{"Welcome!"},
Id: 2,
}

You have to init like this:
p := &Article{
Page: Page{
Title: "Welcome!",
},
Id: 2,
}
PlayGround: http://play.golang.org/p/CEUahBLwCT
package main
import "fmt"
type Page struct {
Title string
}
type Article struct {
Page
Id int
}
func main() {
// this generates a build error:
// "invalid field name Title in struct initializer"
//
p := &Article{
Page: Page{
Title: "Welcome!",
},
Id: 2,
}
fmt.Printf("%#v \n", p)
}

Related

How to create the dynamic struct except using interface and Using spread operator

I am trying to create the new API with Go and Fiber framework. I am using MongoDB.
Please refer to my below structure
type Product struct {
Name string `json:"name"`
Code string `json:"code"`
Description string `json:"description"`
Brand string `json:"brand"`
Variants []map[string]string `json:"variants"`
Categories []int `json:"categories"`
}
The Variants field will have another set array of objects. But the fields will be dynamic. So I can't define the structure. Now this one I solved with []map[string][string].
My task here I have to form the CSV file with the below format.
[{
Name: "Rock",
Code: "RRR"
Description: "This is rock"
Brand: "R"
...variants
},
{
Name: "Rock",
Code: "RRR"
Description: "This is rock"
Brand: "R"
...variants
},
{
Name: "Rambo",
Code: "RAM"
Description: "This is Rambo"
Brand: "RA"
...variants
},
{
Name: "Rambo",
Code: "RAM"
Description: "This is Rambo"
Brand: "RA"
...variants
}]
Only variants will change for every row. Others will be remaining same
I formed the other fields except for the Variants. I can't create the struct because the data will be dynamic.
I have a lot of confusion
How to create the struct for dynamic fields
How to spread the Variants at the root level
I am from javascript. So, we used the spread operator. Here I am confused about how to do that in Golang.
Help me out, guys
#mkopriva is right
However you can use reflection to dynamically construct a new struct if you like
follow is a example:
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type DynamicMap map[string]interface{}
type Product struct {
Name string `json:"name"`
Code string `json:"code"`
Description string `json:"description"`
Brand string `json:"brand"`
Variants map[string]string `json:"variants"`
Categories []int `json:"categories"`
}
func (j Product) MarshalJSON() ([]byte, error) {
m := DynamicMap{}
t := reflect.TypeOf(j)
v := reflect.ValueOf(j)
for i := 0; i < t.NumField(); i++ {
if t.Field(i).Tag.Get("json") == "variants" {
dyn := v.Field(i).Interface().(map[string]string)
for key, val := range dyn {
m[key] = val
}
} else if t.Field(i).Type.Kind() == reflect.String {
m[t.Field(i).Tag.Get("json")] = v.Field(i).String()
} else {
m[t.Field(i).Tag.Get("json")] = v.Field(i)
}
}
return json.Marshal(m)
}
func (j Product) UnmarshalJSON(data []byte) error {
fmt.Println("Unmarshal...")
return json.Unmarshal(data, &j)
}
func main() {
p := Product{Name: "aa", Variants: map[string]string{"a": "a", "b": "b"}}
marshal, err := json.Marshal(p)
if err != nil {
fmt.Printf("%v\n", err)
}
fmt.Printf("%s\n", marshal)
}
{"a":"a","b":"b","brand":"","categories":{},"code":"","description":"","name":"aa"}

Reduce a large struct into a small one?

Say I make a call to an api, which returns a slice of structs, each with a load of methods and fields, but I only want to use one field per each element of the returned values. How can I do this?
For instance, I call out to an API, and it returns a slice of x elements, each which has 4 values and 13method, but I only want 1 value and 0 methods (the slice of fetus structs). How can I marshall this into my own struct? eg:
func GETApi() []fetus {
//doGet() returns a slice of persons, which are described below
a := doGet() // this gets many detailed persons as a slice, but I just want them as a slice of fetus
/*
type person struct {
id:
age:
height:
width:
}
func (a *person) GetHeight() int { ... }
func (a *person) GetWidth() int { ... }
func (a *person) GetLaught() int { ... }
// I want to return a slice of these ([]fetus)
type fetus struct {
id:
}
var f fetus
f := a // how can I condense said slice of persons into a slice of fetus
return f
*/
Perhaps something like this?
package main
type Person struct {
Id string
Name string
Age string
LotsOfOtherFields string
}
type Fetus struct {
Id string
}
func main() {
persons := []Person{
{Id: "a", Name: "John"},
{Id: "b", Name: "Steve"},
{Id: "c", Name: "Fred"},
}
fetuses := make([]Fetus, len(persons))
for i, p := range persons {
// Create a new fetus struct and pluck the ID from the person struct
fetuses[i] = Fetus{Id: p.Id}
}
}

Use different structs as a value in map golang

Is there any way to create a map into several structs, and then use It?
I have several different structs that implement the same interface and matching input types for each struct.
I want to read data from the different inputs into the structs - without knowing the input type at the compilation time.
type myInput struct {
InputType string
data []bytes
}
// Will get as an input after compeleation
inputs := []myInput{
myInput{InputType: "a", data: []bytes{0x01, 0x02, 0x03}},
myInput{InputType: "b", data: []bytes{0x01, 0x02}},
}
type StructA struct {
A uint16
B uint32
}
func (a StructA) name () {
fmt.Printf("name is: %d %d", a.A, a.B)
}
type StructB struct {
C uint32
}
func (b StructB) name () {
fmt.Printf("name is: %d", b.C)
}
AorB map[string]<???> {
"a": StructA,
"b": StructB,
}
At this point, I don't know what to do. I need to take the correct struct by the input type and initialize the struct using binary.Read.
for _, myInput := range (inputs) {
// ???? :(
myStruct := AtoB[myInput.InputType]{}
reader :=bytes.NewReader(input1)
err := binary.Read(reader, binary.BigEndian, &myStruct)
fmt.Printf(myStruct.name())
}
Thanks!
Define a interface
type Bin interface {
name() string
set([]byte) // maybe returning error
}
You'll be handling Bins only.
type StructA struct { /* your code so far */ }
type StructB struct { /* your code so far */ }
func (a *StructA) set(b []byte) {
a.A = b[0]<<8 + b[1] // get that right, too lazy to code this for you
a.B = b[2]<<24 + b[3]<<16 + ...
}
// same for StructB
So your StructA/B are Bins now.
func makeBin(in myInput) Bin {
var bin Bin
if in.InputType == "a" {
bin = &StructA{}
} else {
bin = &StructB{}
}
bin.set(in.data) // error handling?
return bin
}
If you have more than two types: Use a switch instead if an if or make a tiny type registry (reflect).
First you define an interface for the commonly used func name:
type Namer interface {
name()
}
Then you can create a map to that interface and insert structs:
AorB := map[string] Namer {
"a": StructA{
A: 42,
B: 28,
},
"b": StructB{
C: 12,
},
}
Now you can access all entries:
for _, n := range AorB {
n.name()
}
You can use interface for the same
AorB := map[string]interface{}{
"a": StructA{},
"b": StructB{},
}
When you retrieve value back you can assert into A for type A and B for type B

error assigning values to struct within a struct golang

I came across this situation where I was trying to assign values to a struct within a struct. There is no compiler error, but it does panic when you run it. Does Go have a different way to handle this data structure?
package main
import (
"fmt"
)
type Label struct {
ID int
Labels []struct {
ID int
Name string
}
}
func main() {
l := Label{}
l.ID = 100
l.Labels[0].ID = 200
l.Labels[0].Name = "me"
fmt.Println(l.ID)
fmt.Println(l.Labels[0].ID)
fmt.Println(l.Labels[0].Name)
}
https://play.golang.org/p/IiuXpaDvF1W
Thanks in advance.
The default value for a slice is nil so it has no elements, and you cannot assign to index 0 because it doesn't exist yet.
You can use append to add a new element to that slice using:
l.Labels = append(l.Labels, struct{
ID int
Name string
}{
ID: 200,
Name: "me",
})
https://play.golang.org/p/uAWdQdh0Ov7
In addition, your use of an inline/anonymous struct here means you'll need to redeclare the type when you append. Consider adding another declared type:
type SubLabel struct {
ID int
Name string
}
type Label struct {
ID int
Labels []SubLabel
}
// ...
l.Labels = append(l.Labels, SubLabel{
ID: 200,
Name: "me",
})
https://play.golang.org/p/4idibGH6Wzd

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

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?
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.
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
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: "gilles#example.com",
},
}
resource := &UserResource{}
deepcopier.Copy(user).To(resource)
//copied from User's Name field
fmt.Println(resource.DisplayName)//output: gilles
fmt.Println(resource.Email) //output: gilles#example.com
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.
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)
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}]}

Resources