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
Related
Lets say I have a struct like this (dummy example) :
type Animal struct {
name string
weight int
isLandAnimal boolean
Family
}
type Family struct {
name string
type string
}
Now what I want to achieve is something like this :
var a Animal = ....
bys, err := json.Marshal(a, []string{"name", "Family.name"})
and once printed bys should look like this :
{
"name": "Lion"
"family" : {
"name" : "Felines"
}
}
So I can pass a string slice that references which fields I want to actually get marshaled in the final string.
Does something like this exists in the standard library or in a third party one?
NOTE: This is what I could do based on another SO answer:
https://go.dev/play/p/XVb83zoXpmb
Will this bring troubles down the road?
You can annotate the fields which let you specify how it should show up in json and if it needs to be omitted when empty. https://pkg.go.dev/encoding/json#Marshal
Here is a working code. https://pkg.go.dev/encoding/json#Marshal
package main
import (
"encoding/json"
"fmt"
)
type Animal struct {
Name string `json:"name,omitempty"`
Weight int `json:"weight,omitempty"`
IsLandAnimal bool `json:"IsLandAnimal,omitempty"`
Family Family `json:"Family,omitempty"`
}
type Family struct {
Name string `json:"Name,omitempty"`
Familytype string `json:"Familytype,omitempty"`
}
func main() {
a := Animal{Family: Family{Name: "Felines"}, Name: "Lion"}
bys, _ := json.Marshal(a)
fmt.Println(string(bys))
}
What you're looking for is struct field annotations for json.Marshal. Basically go expects you to explicitly define the mapping from a struct to your export object, in this case json.
You need to ensure that the fields in your struct are exported, or basically start with a capital letter.
Next you can add a annotation to the end of the field to tell go what to look for when unmarshalling, or how to marshal that specific field into the file type format you're looking for.
For example:
This struct with exported members and annotations:
type Cookie struct {
Name string `json:"name"`
Ingredients int `json:"ingredients"`
MilkDunkTime int `json:"milk_dunk_time"`
}
would export into this json:
{
"name": "",
"ingredients: 0,
"milk_dunk_time:" 0
}
I've provided a playground link for your example above with some minor tweaks.
I have an interface{} that is similar like -
Rows interface{}
In the Rows interface i put ProductResponse struct.
type ProductResponse struct {
CompanyName string `json:"company_name"`
CompanyID uint `json:"company_id"`
CompanyProducts []*Products `json:"CompanyProducts"`
}
type Products struct {
Product_ID uint `json:"id"`
Product_Name string `json:"product_name"`
}
I want to access Product_Name value. How to access this.
I can access outside values (CompanyName , CompanyID) by using "reflect" pkg.
value := reflect.ValueOf(response)
CompanyName := value.FieldByName("CompanyName").Interface().(string)
I am not able to access Products struct values. How to do that?
You can use type assertion:
pr := rows.(ProductResponse)
fmt.Println(pr.CompanyProducts[0].Product_ID)
fmt.Println(pr.CompanyProducts[0].Product_Name)
Or you can use the reflect package:
rv := reflect.ValueOf(rows)
// get the value of the CompanyProducts field
v := rv.FieldByName("CompanyProducts")
// that value is a slice, so use .Index(N) to get the Nth element in that slice
v = v.Index(0)
// the elements are of type *Product so use .Elem() to dereference the pointer and get the struct value
v = v.Elem()
fmt.Println(v.FieldByName("Product_ID").Interface())
fmt.Println(v.FieldByName("Product_Name").Interface())
https://play.golang.org/p/RAcCwj843nM
Instead of using reflection you should use type assertion.
res, ok := response.(ProductResponse)
if ok { // Successful
res.CompanyProducts[0].Product_Name // Access Product_Name or Product_ID
} else {
// Handle type assertion failure
}
You can access Product_Name value without even using "reflect" pkg by simply iterating over the CompanyProducts slice by using for loop.I have created a simple program for you scenario as follows:
package main
import (
"fmt"
)
type ProductResponse struct {
CompanyName string `json:"company_name"`
CompanyID uint `json:"company_id"`
CompanyProducts []*Products `json:"CompanyProducts"`
}
type Products struct {
Product_ID uint `json:"id"`
Product_Name string `json:"product_name"`
}
func main() {
var rows2 interface{} = ProductResponse{CompanyName: "Zensar", CompanyID: 1001, CompanyProducts: []*Products{{1, "prod1"}, {2, "prod2"}, {3, "prod3"}}}
for i := 0; i < len(rows2.(ProductResponse).CompanyProducts); i++ {
fmt.Println(rows2.(ProductResponse).CompanyProducts[i].Product_Name)
}
}
Output:
prod1
prod2
prod3
I had an Open API generated schema, and I would like to create the response struct in my handler function. But when I want to create the anonymous struct, I need to write again the field tags, too. Is there any solution for not needing to repeat that? Here is the minimal example
package main
// Response is generated by a tool in a separated file
type Response struct {
Result *struct {
Id int `json:"id"`
} `json:"result,omitempty"`
}
func main() {
var response Response
response.Result = &struct {
Id int `json:"id"`
}{5}
// This results in an error
// cannot use &struct { Id int } literal (type *struct { Id int }) as type *struct { Id int "json:\"id\"" } in assignment
response.Result = &struct {Id int}{5}
}
This anonymous struct is a result of oapi-codegen, when I use a complex type in the components part of the api definition as a response type. I think if I use a CreateBookResponseResult type, the code generator won't generate an anonymous struct, but I would postpone this option until I can.
components:
CreateBookResponse:
type: object
properties:
result:
type: object
properties:
id:
type: integer
Use a named type.
type Response struct {
Result *ResponseResult `json:"result,omitempty"`
}
type ResponseResult struct {
Id int `json:"id"`
}
var response Response
response.Result = &ResponseResult{Id: 5}
I have the following structs...
type Menu struct {
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"`
Description string `protobuf:"bytes,3,opt,name=description" json:"description,omitempty"`
Mixers []*Mixer `protobuf:"bytes,4,rep,name=mixers" json:"mixers,omitempty"`
Sections []*Section `protobuf:"bytes,5,rep,name=sections" json:"sections,omitempty"`
}
And...
type Menu struct {
ID bson.ObjectId `json:"id" bson:"_id"`
Name string `json:"name" bson:"name"`
Description string `json:"description" bson:"description"`
Mixers []Mixer `json:"mixers" bson:"mixers"`
Sections []Section `json:"sections" bson:"sections"`
}
I basically need to convert between the two struct types, I've attempted to use mergo, but that can only merge structs that are assignable to one another. The only solution I have so far is iterating through each struct, converting the ID by re-assigning it and converting its type between string and bson.ObjectId. Then iterating through each map field and doing the same. Which feels like an inefficient solution.
So I'm attempting to use reflection to be more generic in converting between the two ID's. But I can't figure out how I can effectively merge all of the other fields that do match automatically, so I can just worry about converting between the ID types.
Here's the code I have so far...
package main
import (
"fmt"
"reflect"
"gopkg.in/mgo.v2/bson"
)
type Sub struct {
Id bson.ObjectId
}
type PbSub struct {
Id string
}
type PbMenu struct {
Id string
Subs []PbSub
}
type Menu struct {
Id bson.ObjectId
Subs []Sub
}
func main() {
pbMenus := []*PbMenu{
&PbMenu{"1", []PbSub{PbSub{"1"}}},
&PbMenu{"2", []PbSub{PbSub{"1"}}},
&PbMenu{"3", []PbSub{PbSub{"1"}}},
}
newMenus := Serialise(pbMenus)
fmt.Println(newMenus)
}
type union struct {
PbMenu
Menu
}
func Serialise(menus []*PbMenu) []Menu {
newMenus := []Menu{}
for _, v := range menus {
m := reflect.TypeOf(*v)
fmt.Println(m)
length := m.NumField()
for i := 0; i < length; i++ {
field := reflect.TypeOf(v).Field(i)
fmt.Println(field.Type.Kind())
if field.Type.Kind() == reflect.Map {
fmt.Println("is map")
}
if field.Name == "Id" && field.Type.String() == "string" {
// Convert ID type
id := bson.ObjectId(v.Id)
var dst Menu
dst.Id = id
// Need to merge other matching struct fields
newMenus = append(newMenus, dst)
}
}
}
return newMenus
}
I'm can't just manually re-assign the fields because I'm hoping to detect maps on the structs fields and recursively perform this function on them, but the fields won't be the same on embedded structs.
Hope this makes sense!
I think that it is probably better to write your own converter, because you will always have some cases that are not covered by existing libs\tools for that.
My initial implementation of it would be something like this: basic impl of structs merger
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}]}