Obtaining the name of a known struct field - go

I have a struct which represents an object in a database, something like:
type Object struct {
Id string
Field1 string
Field2 int
}
And I'd like to have a function that updates the specific field in the database whenever the field is modified, something along these lines:
func (self *Object) SetField1(value string) {
self.Field1 = value
database.Update(self.Id, "Field1", self.Field1) // pseudocode
}
Is there a way to replace the "Field1" hard-coded string such that my code is resistant to future changes in the struct field ordering and naming?
I've poked around the reflect package, and it would be nice to be able to get the StructField that represents the field I'm working with, but it seems to require either the name of the field via hard-coded string, or the field's index in the struct (which is subject to change).

not in the context that you're talking about. It's not passed in as a parameter, so you need some other way of specifying which of the struct's fields to be sent. The mental gap here is you're trying to treat that set function like it's a property when it's not; the key difference between a property, as seen in other languages, is that a property is bound to a specific field, whereas your SetField1 method is bound to the whole struct. That method might as well set two fields.
Generally if you're doing field-wise reflective stuff, and you want to do fancy dynamic stuff with fields, you want to use struct tags. e.g., like this:
type Object struct {
Id string `db:"id"`
Field1 string `db:"field1"`
Field2 int `db:"field2"`
}
you can access those tags like-a-this:
package main
import (
"reflect"
"fmt"
)
func main() {
var x struct {
MyField int `core:"required"`
}
t := reflect.TypeOf(x)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fmt.Println(field.Name, ":", field.Tag.Get("core"))
}
}
that's not really a full solution, but... the question in the way it's asked is impossible to do.

You could validate your string by adding a method something like this:
func (self *Object) verify(field string) string {
if _, ok := reflect.TypeOf(*self).FieldByName(field); ok {
return field
}
panic("Invalid field name")
}
And then use it when passing the string to the database update
func (self *Object) SetField1(value string) {
self.Field1 = value
database.Update(self.Id, self.verify("Field1"), self.Field1) // pseudocode
}
But I would think that if you're willing to use reflection, that you'd be better off just making a generic setField method that accepts the field as a string, and the value as a interface{}, checks the field and value, sets the value and updates the database.
This way everything is done using the string, so it'll either work or panic, and you don't need to remember to use the .verify() method.
Somthing like:
func (self *Object) SetField(field string, value interface{}) {
// verify field and value using reflection
// set the value using reflection
database.Update(self.Id, field, self.Field1)
}
Though I don't think this'll work on unexported fields.

Related

Creating array of struct dynamically in golang

I try to create a generic function that accepts any struct value and create a array of that struct type. Here is the code I tried. But I get the error "t is not a type". How can I implement this.
type RegAppDB struct {
nm string
data []interface{}
}
func CreateRegTable(tbl string, rec interface{}) RegAppDB {
t := reflect.TypeOf(rec)
fmt.Println(t)
return RegAppDB{"log", []t}
}
Go does not support generics, and any attempt to do something like that is not going to work out well. In your specific case, there are a couple of key problems:
You cannot use a variable as a type. Go is compile-time static typed, so anything that gets type information at runtime (i.e. reflect.TypeOf) happens too late to use the way you're trying to do it.
Equally important, your struct's field is of type []interface{}, which means the only type you can use for that field is []interface{}. []string, for example, is a different type, and cannot be assigned to that field.
I took another route. Need some beautification. But this works. So now data is an array of interface and from calling function I pass pointer to structure variables to Save function.
type RegAppDB struct {
nm string
data []interface{}
cnt int
}
// CreateRegTable creates a data structure to hold the regression result
func CreateRegTable(tbl string) *RegAppDB {
return &RegAppDB{tbl, make([]interface{}, 20), 0}
}
// Save implements saving a record Regression application DB
func (rga *RegAppDB) Save(rec interface{}) error {
rga.data[rga.cnt] = rec
rga.cnt++
return nil
}
// Show implements showing the regression table
func (rga *RegAppDB) Show() error {
fmt.Println(rga.cnt)
for i := 0; i <= rga.cnt; i++ {
fmt.Println(rga.data[i])
}
return nil
}
// Compare compares two regression table for equality
func (rga *RegAppDB) Compare(rgt *RegAppDB) bool {
return reflect.DeepEqual(rga, rgt)
}
It cannot be done for a generic type. If there are a fixed number of possible types, then you can do something like the following:
type RegAppDB struct {
nm string
data interface{}
}
func CreateRegTable(rec interface{}) RegAppDB {
switch rec.(type) {
case int:
return &RegAppDB{"log", []int{}}
case string:
return &RegAppDB{"log", []string{}}
}
return nil
}
Note: Your data in RegAppDB should be of type interface{} since []int implements interface{} but not []interface{}

How to pass a pointer of struct field in mapstructure.decode

I am trying to decode a map into a struct type with help of mapstructure library. If I do it with plain variable it decodes ok, but if I pass struct field it does not decode the map:
package main
import (
"github.com/mitchellh/mapstructure"
)
type Person struct {
Name string
}
type Bundle struct {
Name string
Struct interface{}
}
func main() {
p_map := map[string]string{
"Name": "John",
}
p := Person{}
mapstructure.Decode(p_map, &p)
print(p.Name) // shows name John
b := Bundle{
"person"
Person{},
}
mapstructure.Decode(p_map, &b.Struct)
print(b.Struct.(Person).Name) // Does not show any name. Blank
}
Could you please clarify if I am passing wrong storage for map decoding or it is just mapstructure limitation and I am not able to decode maps into struct fields? Thank you!
UPD
I am sorry if I was not clear enough about the actual reason I need to use such flow:
I send HTTP requests to different resources and get various objects with different fields so initially I collect them as interface{}. After I get a particular resource object, I need to convert it into a particular struct (Person in my sample) so I use mapstructure.decode() function for that.
As I have various objects that are decoded in different structures I want to create a loop in order to avoid code duplication. What I wanted to do is to create a slice with different structures like:
bundles := []Bundle{
{"person", Person{}}
{"employee", Employee{}}
...
}
And then decode objects in a loop:
for bundle := range bundles {
// map_storage contains different type maps that are going to be decoded into struct and key for the specific object is bundle name
mapstructure.Decode(maps_storage[bundle.Name], &bundle.Struct)
// bundle.Struct blank, but I expect that it is filled as in the example below
}
I think you must slightly change the implementation
var p1 Person
mapstructure.Decode(p_map, &p1)
b := Bundle{
p1,
}
print(b.Struct.(Person).Name) // John will appear
I'm trying your code above but lead to empty Person. Maybe Decode function cannot change real value of b.Struct(I'm not sure the exact reason, this is just my opinion), but if I decode to struct Person first then assign to Bundle that works.
Updated:
with some research, I found out the problem.You must use pointer instead of struct. here the updated code
package main
import (
"github.com/mitchellh/mapstructure"
)
type Person struct {
Name string
}
type Bundle struct {
Name string
Struct interface{}
}
func main() {
p_map := map[string]string{
"Name": "John",
}
p := &Person{}
mapstructure.Decode(p_map, &p)
print(p.Name) // shows name John
b := Bundle{
"person",
&Person{},
}
mapstructure.Decode(p_map, &b.Struct)
print(b.Struct.(*Person).Name) // Does not show any name. Blank
}
After changing the type of Struct field in the Bundle from interface{} to Person, It worked for me.
type Bundle struct {
Struct Person
}
print(b.Struct.Name)

Golang Factory Method

I am learning golang, and am getting stuck on an embarassingly simple concept. Maybe it's my OO habits clouding my comprehension, but I can't seem to get this simple example to work:
package main
import (
"fmt"
)
type datafield struct {
name string
value string
}
func (d datafield) NewField(name, value string) *datafield {
retval := new(datafield)
retval.name = name
retval.value = value
return retval
}
func main() {
field := datafield.NewField("name", "value")
if field == nil {
fmt.Println("Error: Did not create a datafield")
} else {
fmt.Println("Success!")
}
}
The error is:
prog.go:20:29: not enough arguments in call to method expression datafield.NewField
have (string, string)
want (datafield, string, string)
What is the proper way to get NewField(string,string) to create a datafield?
Go isn't exactly object oriented language and promotes simplicity, that's why the previous answers are focused on fixing your code. Nevertheless, if you really need to implement this design pattern in Go, read further.
The factory method pattern is a creational pattern that uses factory
methods to deal with the problem of creating objects without having to
specify the exact class of the object that will be created. This is
done by creating objects by calling a factory method. (wikipedia)
Here is another definition and a really good example in Go by Svetlin
Ralchev:
The Factory Method pattern is used to define a runtime interface for
creating an object. It’s called a factory because it creates various
types of objects without necessarily knowing what kind of object it
creates or how to create it. (Desing Patterns in Golang: Factory
Method)
I've extended your example a bit to demonstrate the benefits of using Factory Methods, as there is no need to use factories at all if you're dealing with one struct (an object in OO world).
https://goplay.space/#SOXPmM86GgF
package main
import (
"fmt"
)
type dataField interface {
Print()
}
type dataField1 struct {
name string
value string
}
func (df *dataField1) Print() {
fmt.Println("dataField1 ->", df.name, ":", df.value)
}
type dataField2 struct {
name string
value string
}
func (df *dataField2) Print() {
fmt.Println("dataField2 ->", df.name, ":", df.value)
}
type dataFieldFactory interface {
Create(name, value string) dataField
}
type dataField1Factory struct{}
func (factory *dataField1Factory) Create(name, value string) dataField {
return &dataField1{
name: name,
value: value,
}
}
type dataField2Factory struct{}
func (factory *dataField2Factory) Create(name, value string) dataField {
return &dataField2{
name: name,
value: value,
}
}
type Document struct {
dataFieldFactories []dataFieldFactory
allValues [][]string
}
func (doc *Document) Print() {
for i, factory := range doc.dataFieldFactories {
field := factory.Create(doc.allValues[i][0], doc.allValues[i][1])
field.Print()
}
}
func main() {
doc := &Document{
dataFieldFactories: []dataFieldFactory{
&dataField1Factory{},
&dataField2Factory{},
},
allValues: [][]string{{"name1", "value1"}, {"name2", "value2"}},
}
doc.Print()
}
The program simply prints this
dataField1 -> name1 : value1
dataField2 -> name2 : value2
However, if you look into main func you don't find any mentions or initializations of concrete types dataField1 and dataField2.
All the complexity is hidden behind dataFieldFactories.
Both dataField1Factory and dataField2Factory implement Create interface and return dataField interface, that both the concrete types
also implement. So, you can call Print() for each of the concrete types.
Document.Print() uses both the Create and Print interfaces for printing
out all the fields without any knowledge about how the fields are actually created or printed. We're achieving this by providing a list of Factory Methods (dataField1Factory{} and dataField2Factory{}) and corresponding string values to the Document struct (object).
Excuse me for a bit artificial example, but I hope, you'll get the basic idea.
As you can see Go allows you to implement design patterns you're familiar with, perhaps, not in exact same way as you get used to in pure OO languages.
You must not set a method on type "datafield" in your case, but do this instead :
func NewField(name, value string) *datafield {
retval := new(datafield)
retval.name = name
retval.value = value
return retval
}
By adding (d datafield) onto the function signature you are making the datafield a receiver argument (https://tour.golang.org/methods/1) so the NewDatafield is now a method on datafield. This means to call it you need to already have a datafield which didn't make much sense so you could just remove the (d datafield) and it will work or even better just construct the stuct directly (https://gobyexample.com/structs):
field := datafield{"name", "value"}
You only really need constructor functions when there is extra work to be done, not just setting fields.

How to handle slices of structs that encapsulate another type as parameters

I have two structs (Dimension and Metric) and part of their attributes overlap, so I decided to go with a common struct that is then encapsulated.
type Attribute struct {
Function *string
Id *string
}
type Dimension struct {
Attribute
Class *string
}
type Metric struct {
Alias *string
Attribute
}
What I would like is to have a function that takes a slice of either Dimensions or a slice of Metrics and sorts it by the id field, which is common between the two.
dimensions := []Dimension{...}
metrics := []Metric{...}
sortById(dimensions)
sortById(metrics)
I could use a slice of interface types func sortItems(items []interface{}) but I would prefer to avoid this so I was wondering how I can do something like the lines below.
func sortItems(items []Attribute) {
//Sort here
}
If I try this, I am getting cannot use metrics (type []Metric) as type []Attribute in argument to this.sortItems which is to be expected, but I am new enough to Go not to know how to approach this.
I know I could just create two functions (one for each type) but I am curious what the correct pattern is for tackling issues like these in Go.
Define an interface and have it be implemented by the common type Attribute.
Something like this:
type Attributer interface {
GetId() *int
}
func (a Attribute) GetId() *int {
return a.Id
}
func sortItems(items []Attributer) {
//Sort here
}
By virtue of embedding the Attribute type, both Dimension and Metric can be used wherever the Attributer interface is expected.
So this will compile just fine.
items := []Attributer{Dimension{}, Metric{}}

Golang: loop through fields of a struct modify them and and return the struct?

I am trying to loop through the individual fields of a struct applying a function to each field and then return the original struct as a whole with the modified field values.
Obviously, this would not present a challenge if it were for one struct but I need the function to be dynamic.
For this instance, I am referencing the Post and Category struct as shown below
type Post struct{
fieldName data `check:"value1"
...
}
type Post struct{
fieldName data `check:"value2"
...
}
I then have a switch function that loops through the respective fields of the structs and depending on what value the check has, applies a function to that field's data as follows
type Datastore interface {
...
}
func CheckSwitch(value reflect.Value){
//this loops through the fields
for i := 0; i < value.NumField(); i++ { // iterates through every struct type field
tag := value.Type().Field(i).Tag // returns the tag string
field := value.Field(i) // returns the content of the struct type field
switch tag.Get("check"){
case "value1":
fmt.Println(field.String())//or some other function
case "value2":
fmt.Println(field.String())//or some other function
....
}
///how could I modify the struct data during the switch seen above and then return the struct with the updated values?
}
}
//the check function is used i.e
function foo(){
p:=Post{fieldName:"bar"}
check(p)
}
func check(d Datastore){
value := reflect.ValueOf(d) ///this gets the fields contained inside the struct
CheckSwitch(value)
...
}
How do I, in essence, re-insert the modified values after the switch statement in CheckSwitch back into the struct specified by the interface in the above example.
Please let me know if you need anything else.
Thanks
The variable field has type reflect.Value. Call the Set* methods on field to set the fields in the struct. For example:
field.SetString("hello")
sets the the struct field to "hello".
You must pass a pointer to the struct if you want to retain the values:
function foo(){
p:=Post{fieldName:"bar"}
check(&p)
}
func check(d Datastore){
value := reflect.ValueOf(d)
if value.Kind() != reflect.Ptr {
// error
}
CheckSwitch(value.Elem())
...
}
Also, the field names must be exported.
playground example

Resources