Issue with nested struct in Go - go

I have two structs A & B and a nested struct C with A & B, defined as below:
Struct A :
type Source_a struct{
Sname string
price float64
Qty int
}
Struct B :
type Source_b struct{
Sname2 string
price2 float64
Qty2 int
}
Nested Struct C :
type Data struct{
S_a []Source_a
S_b []Source_b
}
I have declared the Source_a & Source_b type var and derived & assigned the values to it from DB. Sample code for Struct A:
//Post DB query
sks := make([]Source_a, 0)
for rows.Next(){
sk := Source_a{}
err := rows.Scan(&sk.Sname, &sk.Uprice, &sk.Qty)
sks = append(sks, sk)
I am having trouble in binding the values of Struct A & B to Struct C. I am trying something but it is throwing errors, pretty sure this is not the correct way:
td := Data{
S_a: []Source_a{
Source_a{
Sname:sks.Sname,
Uprice:sks.Uprice,
Qty:sks.Qty,
},
},
S_b: []Source_b{
Source_b{
Sname2: sks2.Sname2,
Uprice2: sks2.Uprice2,
Qty2: sks2.Qty2,
},
},
},
Can you please help, I am new to Golang. Let me know if you need clarifications or specifics.

sks and sks2 seem like slices and you are using them as variables. You can do
td := Data{
S_a: sks,
S_b: sks2,
}

Related

Golang count number of fields in a struct of structs

Given a struct CompleteStruct that is composed of two embedded structs StructA and StructB where StructB has ImbStructC in it.
type StructA struct {
AA int
AB int
AC int
}
type ImbStructC struct {
BCC int
}
type StructB struct {
BA int
BB int
ImbStructC
}
type CompleteStruct struct {
StructA
StructB
}
How do you extract the total number of fields in the inner struct?
reflect.TypeOf(CompleteStruct{}).NumField())
returns 2, I assume because CompleteStruct is made up of 2 embedded structs.
What code can I use to show that CompleteStruct has 6 fields?
Recursion is needed to solve this, as an embedded struct field may itself embed another struct.
Also, one should be careful not to count embedded structs as field - these are listed as "anonymous" fields in the reflect package:
func countFields(v any) int {
return rvCountFields(reflect.ValueOf(v))
}
func rvCountFields(rv reflect.Value) (count int) {
if rv.Kind() != reflect.Struct {
return
}
fs := rv.NumField()
count += fs
for i := 0; i < fs; i++ {
f := rv.Field(i)
if rv.Type().Field(i).Anonymous {
count-- // don't count embedded structs (listed as anonymous fields) as a field
}
// recurse each field to see if that field is also an embedded struct
count += rvCountFields(f)
}
return
}
https://go.dev/play/p/IjOllo86_xk
Output:
main.CompleteStruct : count = 5
main.StructA : count = 3
main.StructB : count = 2
main.StructC : count = 6
main.StructD : count = 12
main.Empty : count = 0
int : count = 0
The reflect.VisibleFields package lists all the fields in the struct, from there is "just" a matter of counting those fields that are not Anonymous.
func CountNumberOfFieldsInAStruct(obj interface{}) int {
fields := reflect.VisibleFields(reflect.TypeOf(obj))
count := 0
for _, field := range fields {
if !field.Anonymous {
count += 1
}
}
return count
}
As #colm.anseo mentions "this will only expose the "visible" exported i.e. capitalized struct field names. It will not include lowercase (unexported) struct fields. So if you only want exported fields, then this would work"
Tested here
https://go.dev/play/p/Ea-y8YAkcqZ
Basically you need to get the number of fields from the parent struct and then loop through the children and get the number of fields for each child and then add them up
innerFields := 0
numOfFields := reflect.TypeOf(CompleteStruct{}).NumField()
for i := 0; i < numOfFields; i++ {
innerFields += reflect.TypeOf(CompleteStruct{}).Field(i).Type.NumField()
}
This code is tested and works

Using reflect, how to set value to a struct field (pointer)

I try to set value to a struct field (pointer field) by reflect, but failed.
I get the name of a struct field, so use FieldByName to get the field
The field is a pointer.
I try to use FieldByName().Set FieldByName().SetPointer to set value.
type t struct {
b *int
}
func main() {
t := new(ts)
a := new(int)
ss := reflect.ValueOf(t).FieldByName("b").Set(a)
}
type t struct {
b *int
}
func main() {
t := new(ts)
a := new(int)
ss := reflect.ValueOf(t).FieldByName("b").SetPointer(a)
}
First code:
=======>
./test.go:14:50: cannot use a (type *int) as type reflect.Value in argument to reflect.ValueOf(t).FieldByName("b").Set
./test.go:14:50: reflect.ValueOf(t).FieldByName("b").Set(a) used as value
Second code:
=======>
./test.go:14:57: cannot use a (type *int) as type unsafe.Pointer in argument to reflect.ValueOf(t).FieldByName("b").SetPointer
./test.go:14:57: reflect.ValueOf(t).FieldByName("b").SetPointer(a) used as value
I want to use reflect to make the pointer field (name "b") alloced a space and set a value.
type ts struct {
B *int //field must be exported
}
func main() {
var t ts
foo := 5
a := &foo
//Use Elem() to indirect through the pointer and get struct field
//Use reflect.ValueOf(a) to satisfy Set() signature
reflect.ValueOf(&t).Elem().FieldByName("B").Set(reflect.ValueOf(a))
fmt.Println(*t.B)
}
Playground https://play.golang.org/p/gZt0ahTZMIi

append a struct B (which inherits from the struct A) to a slice of structs A

I have a struct B which inherits from the struct A. I have another struct C (which contains a slice of structs A) and I want to append B to C.
package main
type A struct {
target string
}
type B struct{
A
values []int
}
type C struct{
Cols []*A
}
func main() {
var values = []int{1,2,3}
var col1 = C{}
var col2 = &B {
A: A{
target: "txt",
},
values: values,
}
col1.Cols = append(col1.Cols, col2)
}
When running this code, it generates an error: cannot use col2 (type *B) as type *A in append
What's wrong please ? I'm newer
Ps: sorry for my bad English
col1.Cols is type *A, col2 is type *B, col2.A is type A, if you want to add new element to the slices, they should be of the same type.
so if you change the last statement to
col1.Cols = append(col1.Cols, &col2.A)
it will work.

How to dynamically create a struct with one less property?

Is there a way to copy a generic struct (i.e. a struct whose property names are unknown) and skip a single, known property?
Here is what I know:
The parameter to my function--I will call the parameter myData-- is of type interface{}.
myData is a struct.
myData has a known property path.
myData has anywhere from 0 to 6 or so other properties, none of which are known a priori.
Once I remove that path property, then the “leftover” is one of say 30 possible struct types.
So I want to strip path out of myData (or more accurately make a copy that omits path) so that various bits of generated code that try to coerce the struct to one of its possible types will be able to succeed.
I have found examples of copying a struct by reflection, but they typically create an empty struct of the same underlying type, then fill it in. So is it even possible to delete a property as I have outlined...?
You can use reflect.StructOf to dynamically create structs from a list of fields.
package main
import (
"fmt"
"reflect"
)
type A struct {
Foo string
Bar int
Baz bool // to be skipped
}
type B struct {
Foo string
Bar int
}
func main() {
av := reflect.ValueOf(A{"hello", 123, true})
fields := make([]reflect.StructField, 0)
values := make([]reflect.Value, 0)
for i := 0; i < av.NumField(); i++ {
f := av.Type().Field(i)
if f.Name != "Baz" {
fields = append(fields, f)
values = append(values, av.Field(i))
}
}
typ := reflect.StructOf(fields)
val := reflect.New(typ).Elem()
for i := 0; i < len(fields); i++ {
val.Field(i).Set(values[i])
}
btyp := reflect.TypeOf(B{})
bval := val.Convert(btyp)
b, ok := bval.Interface().(B)
fmt.Println(b, ok)
}

How to access unexported struct fields

Is there a way to use reflect to access unexported fields in Go 1.8?
This no longer seems to work: https://stackoverflow.com/a/17982725/555493
Note that reflect.DeepEqual works just fine (that is, it can access unexported fields) but I can't make heads or tails of that function. Here's a go playarea that shows it in action: https://play.golang.org/p/vyEvay6eVG. The src code is below
import (
"fmt"
"reflect"
)
type Foo struct {
private string
}
func main() {
x := Foo{"hello"}
y := Foo{"goodbye"}
z := Foo{"hello"}
fmt.Println(reflect.DeepEqual(x,y)) //false
fmt.Println(reflect.DeepEqual(x,z)) //true
}
If the struct is addressable, you can use unsafe.Pointer to access the field (read or write) it, like this:
rs := reflect.ValueOf(&MyStruct).Elem()
rf := rs.Field(n)
// rf can't be read or set.
rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem()
// Now rf can be read and set.
See full example on the playground.
This use of unsafe.Pointer is valid according to the documentation and running go vet returns no errors.
If the struct is not addressable this trick won't work, but you can create an addressable copy like this:
rs = reflect.ValueOf(MyStruct)
rs2 := reflect.New(rs.Type()).Elem()
rs2.Set(rs)
rf = rs2.Field(0)
rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem()
// Now rf can be read. Setting will succeed but only affects the temporary copy.
See full example on the playground.
Based on cpcallen's work:
import (
"reflect"
"unsafe"
)
func GetUnexportedField(field reflect.Value) interface{} {
return reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem().Interface()
}
func SetUnexportedField(field reflect.Value, value interface{}) {
reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).
Elem().
Set(reflect.ValueOf(value))
}
reflect.NewAt might be confusing to read at first. It returns a reflect.Value representing a pointer to a value of the specified field.Type(), using unsafe.Pointer(field.UnsafeAddr()) as that pointer. In this context reflect.NewAt is different than reflect.New, which would return a pointer to a freshly initialized value.
Example:
type Foo struct {
unexportedField string
}
GetUnexportedField(reflect.ValueOf(&Foo{}).Elem().FieldByName("unexportedField"))
https://play.golang.org/p/IgjlQPYdKFR
reflect.DeepEqual() can do it because it has access to unexported features of the reflect package, in this case namely for the valueInterface() function, which takes a safe argument, which denies access to unexported field values via the Value.Interface() method if safe=true. reflect.DeepEqual() will (might) call that passing safe=false.
You can still do it, but you cannot use Value.Interface() for unexported fields. Instead you have to use type-specific methods, such as Value.String() for string, Value.Float() for floats, Value.Int() for ints etc. These will return you a copy of the value (which is enough to inspect it), but will not allow you to modify the field's value (which might be "partly" possible if Value.Interface() would work and the field type would be a pointer type).
If a field happens to be an interface type, you may use Value.Elem() to get to the value contained / wrapped by the interface value.
To demonstrate:
type Foo struct {
s string
i int
j interface{}
}
func main() {
x := Foo{"hello", 2, 3.0}
v := reflect.ValueOf(x)
s := v.FieldByName("s")
fmt.Printf("%T %v\n", s.String(), s.String())
i := v.FieldByName("i")
fmt.Printf("%T %v\n", i.Int(), i.Int())
j := v.FieldByName("j").Elem()
fmt.Printf("%T %v\n", j.Float(), j.Float())
}
Output (try it on the Go Playground):
string hello
int64 2
float64 3
package main
import (
"fmt"
"reflect"
"strings"
"unsafe"
)
type Person1 struct {
W3ID string
Name string
}
type Address1 struct {
city string
country string
}
type User1 struct {
name string
age int
address Address1
manager Person1
developer Person1
tech Person1
}
func showDetails(load, email interface{}) {
if reflect.ValueOf(load).Kind() == reflect.Struct {
typ := reflect.TypeOf(load)
value := reflect.ValueOf(load)
//#1 For struct, not addressable create a copy With Element.
value2 := reflect.New(value.Type()).Elem()
//#2 Value2 is addressable and can be set
value2.Set(value)
for i := 0; i < typ.NumField(); i++ {
if value.Field(i).Kind() == reflect.Struct {
rf := value2.Field(i)
/* #nosec G103 */
rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem()
irf := rf.Interface()
typrf := reflect.TypeOf(irf)
nameP := typrf.String()
if strings.Contains(nameP, "Person") {
//fmt.Println(nameP, "FOUND !!!!!!! ")
for j := 0; j < typrf.NumField(); j++ {
re := rf.Field(j)
nameW := typrf.Field(j).Name
if strings.Contains(nameW, "W3ID") {
valueW := re.Interface()
fetchEmail := valueW.(string)
if fetchEmail == email {
fmt.Println(fetchEmail, " MATCH!!!!")
}
}
}
}
showDetails(irf, email)
} else {
// fmt.Printf("%d.Type:%T || Value:%#v\n",
// (i + 1), value.Field(i), value.Field(i))
}
}
}
}
func main() {
iD := "tsumi#in.org.com"
load := User1{
name: "John Doe",
age: 34,
address: Address1{
city: "New York",
country: "USA",
},
manager: Person1{
W3ID: "jBult#in.org.com",
Name: "Bualt",
},
developer: Person1{
W3ID: "tsumi#in.org.com",
Name: "Sumi",
},
tech: Person1{
W3ID: "lPaul#in.org.com",
Name: "Paul",
},
}
showDetails(load, iD)
}

Resources