How populate custom struct type from interface{} returned from reflections.GetField()? - go

My configuration (populated via viper) includes the following:
Puppet struct {
Tasks struct {
Task1 struct {
FullTaskName string
}
Task2 struct {
FullTaskName string
}
}
}
My code seeks to extract attributes for the appropriate task dynamically.
reflections.GetField() returns an empty interface, so in order to access the 'FullTaskName' attribute later on (say with 'pt.FullTaskName') I therefore need to either convert it, or assert, or something, to my custom type (as I poorly understand it). Here I'm trying to convert.
type pTask struct {
FullTaskName string
}
taskName := "Task1"
allTasks := conf.Puppet.Tasks
t, _ := reflections.GetField(allTasks, taskName)
pt := pTask(t)
But I can't figure out how to do it. I've confirmed via the debugger that the value of 't' is as expected prior to my attempted assignment:
t = {interface{} | struct {...}}
FullTaskName = {string} "foobar"
However I get the following error when trying to assign & convert 't' to 'pt':
"Cannot convert an expression of the type 'interface{}' to the type 'pTask'"
I've tried asserting it as well but it also fails ('ok' == false).
It looks to me like my custom type matches the values in the returned interface{}, in that they have the same attributes with the same names. There are no methods attached to my 'pTask' type. What am I not understanding?

Related

Get the type name of a generic struct without type parameters

Say I have a generic struct called foo and I create two objects from it. I can determine the concrete type of each using reflect.TypeOf(), like so:
package main
import (
"fmt"
"reflect"
)
type foo[T any] struct {
data T
}
func main() {
a := foo[string]{"cheese"}
b := foo[int]{42}
fmt.Println(reflect.TypeOf(a))
fmt.Println(reflect.TypeOf(b))
}
// main.foo[string]
// main.foo[int]
What I am interested in is determining just the generic type of these objects (i.e., foo) and not the concrete type (i.e., foo[string] and foo[int]). Is this possible or do I need to manually extract the generic type from these strings (e.g., with regex)?
Edit
Regex might look something like this:
func GetGenericType(x any) string {
// Get type as a string
s := reflect.TypeOf(x).String()
// Regex to run
r := regexp.MustCompile(`\.(.*)\[`)
// Return capture
return r.FindStringSubmatch(s)[1]
}
fmt.Println(GetGenericType(a))
fmt.Println(GetGenericType(b))
// foo
// foo
I've also seen this question but this doesn't answer this question because it gives the concrete type (i.e., main.foo[string]) rather than the generic type (i.e., foo).
Reflection doesn't see the name of the "base" generic type, because at run time that base type doesn't exist.
The relevant passage from the Go spec is Instantiations:
Instantiating a type results in a new non-generic named type; instantiating a function produces a new non-generic function.
So when you write:
b := foo[int]{42}
name := reflect.TypeOf(b).Name()
the name of that type is precisely foo[int].
It's worth noting that the identifier foo without the type parameter list is relevant at compile time, because it prevents you from redeclaring it in the same package. Type definitions:
A type definition creates a new, distinct type with the same
underlying type and operations as the given type and binds an
identifier, the type name, to it.
TypeDef = identifier [ TypeParameters ] Type .
But instantiations, as defined above, result in a new named type which is different than foo; and at run time when you can use reflection, you deal with instantiations only.
In conclusion, I think your solution with regex is acceptable, until some helper function is added to the stdlib (if ever). Reposting it here for clarity:
func GetGenericType(x any) string {
// Get type as a string
s := reflect.TypeOf(x).String()
// Regex to run
r := regexp.MustCompile(`\.(.*)\[`)
// Return capture
return r.FindStringSubmatch(s)[1]
}
Just keep in mind the difference between Type.String() and Type.Name(): any type can have a string representation, but only named types have a name. (Obviously, right?). So for example if you wrote:
b := &foo[int]{42}
then the type of b is *foo[int], which is an anonymous composite type, and Name() returns an empty string.

How to get a struct tag without having to instantiate the struct

type User struct{
Name string `json:"name"`
}
func main() {
t := reflect.TypeOf(User{})
}
It is possible to capture the tag's values ​​without instantiating it. For example capturing the value of the tag "json" without having to instantiate the User structure.
Creating a value is not a performance issue in almost all scenarios, but you can avoid creating a value by using a nil pointer value.
t := reflect.TypeOf((*User)(nil)).Elem() // Elem "dereferences" the pointer type.
The reason to use the pointer pattern shown here is that the one syntax works with every type. In particular, it handles interface types:
type I interface{}
// Naive developers might expect the following
// to return the type for I, but it actually
// returns nil.
t := reflect.TypeOf(I(nil))
// The pointer pattern returns the type for I.
t = reflect.TypeOf((*I)(nil)).Elem()
The pointer syntax is the same for all types independent of how the zero value is written for the type:
t = reflect.TypeOf(User{})
t = reflect.TypeOf(uint(0))
t = reflect.TypeOf(myString(""))
Use t = reflect.TypeOf((*T)(nil)).Elem() where T is any type:
t = reflect.TypeOf((*User)(nil)).Elem()
t = reflect.TypeOf((*uint)(nil)).Elem()
t = reflect.TypeOf((*myString)(nil)).Elem()

Golang get string representation of specific struct field name

I really want a way to print the string representation of a field name in go. It has several use cases, but here is an example:
lets say I have a struct
type Test struct {
Field string `bson:"Field" json:"field"`
OtherField int `bson:"OtherField" json:"otherField"`
}
and, for example, I want to do a mongo find:
collection.Find(bson.M{"OtherField": someValue})
I don't like that I have to put the string "OtherField" in there. It seems brittle and easy to either misstype or have the struct change and then my query fails without me knowing it.
Is there any way to get the string "OtherField" without having to either declare a const or something like that? I know I can use reflection to a get a list of field names from a struct, but I'd really like to do something along the lines of
fieldName := nameOf(Test{}.OtherField)
collection.Find(bson.M{fieldName: someValue})
is there any way to do this in Go?? C# 6 has the built in nameof, but digging through reflection I can't find any way to do this in Go.
I don't really think there is. You may be able to load a set of types via reflection and generate a set of constants for the field names. So:
type Test struct {
Field string `bson:"Field" json:"field"`
OtherField int `bson:"OtherField" json:"otherField"`
}
Could generate something like:
var TestFields = struct{
Field string
OtherField string
}{"Field","OtherField"}
and you could use TestFields.Field as a constant.
Unfortunately, I don't know of any existing tool that does anything like that. Would be fairly simple to do, and wire up to go generate though.
EDIT:
How I'd generate it:
Make a package that accepts an array of reflect.Type or interface{} and spits out a code file.
Make a generate.go somewhere in my repo with main function:
func main(){
var text = mygenerator.Gen(Test{}, OtherStruct{}, ...)
// write text to constants.go or something
}
Add //go:generate go run scripts/generate.go to my main app and run go generate
Here is a function that will return a []string with the struct field names. I think it comes in the order they are defined.
WARNING: Reordering the fields in the struct definition will change the order in which they appear
https://play.golang.org/p/dNATzNn47S
package main
import (
"fmt"
"strings"
"regexp"
)
type Test struct {
Field string `bson:"Field" json:"field"`
OtherField int `bson:"OtherField" json:"otherField"`
}
func main() {
fields, err := GetFieldNames(Test{})
if err != nil {
fmt.Println(err)
return
}
fmt.Println(fields)
}
func GetFieldNames(i interface{}) ([]string, error) {
// regular expression to find the unquoted json
reg := regexp.MustCompile(`(\s*?{\s*?|\s*?,\s*?)(['"])?(?P<Field>[a-zA-Z0-9]+)(['"])?:`)
// print struct in almost json form (fields unquoted)
raw := fmt.Sprintf("%#v", i)
// remove the struct name so string begins with "{"
fjs := raw[strings.Index(raw,"{"):]
// find and grab submatch 3
matches := reg.FindAllStringSubmatch(fjs,-1)
// collect
fields := []string{}
for _, v := range matches {
if len(v) >= 3 && v[3] != "" {
fields = append(fields, v[3])
}
}
return fields, nil
}

How to set default values in Go structs

There are multiple answers/techniques to the below question:
How to set default values to golang structs?
How to initialize structs in golang
I have a couple of answers but further discussion is required.
One possible idea is to write separate constructor function
//Something is the structure we work with
type Something struct {
Text string
DefaultText string
}
// NewSomething create new instance of Something
func NewSomething(text string) Something {
something := Something{}
something.Text = text
something.DefaultText = "default text"
return something
}
Force a method to get the struct (the constructor way).
From this post:
A good design is to make your type unexported, but provide an exported constructor function like NewMyType() in which you can properly initialize your struct / type. Also return an interface type and not a concrete type, and the interface should contain everything others want to do with your value. And your concrete type must implement that interface of course.
This can be done by simply making the type itself unexported. You can export the function NewSomething and even the fields Text and DefaultText, but just don't export the struct type something.
Another way to customize it for you own module is by using a Config struct to set default values (Option 5 in the link). Not a good way though.
One problem with option 1 in answer from
Victor Zamanian is that if the type isn't exported then users of your package can't declare it as the type for function parameters etc. One way around this would be to export an interface instead of the struct e.g.
package candidate
// Exporting interface instead of struct
type Candidate interface {}
// Struct is not exported
type candidate struct {
Name string
Votes uint32 // Defaults to 0
}
// We are forced to call the constructor to get an instance of candidate
func New(name string) Candidate {
return candidate{name, 0} // enforce the default value here
}
Which lets us declare function parameter types using the exported Candidate interface.
The only disadvantage I can see from this solution is that all our methods need to be declared in the interface definition, but you could argue that that is good practice anyway.
There is a way of doing this with tags, which
allows for multiple defaults.
Assume you have the following struct, with 2 default
tags default0 and default1.
type A struct {
I int `default0:"3" default1:"42"`
S string `default0:"Some String..." default1:"Some Other String..."`
}
Now it's possible to Set the defaults.
func main() {
ptr := &A{}
Set(ptr, "default0")
fmt.Printf("ptr.I=%d ptr.S=%s\n", ptr.I, ptr.S)
// ptr.I=3 ptr.S=Some String...
Set(ptr, "default1")
fmt.Printf("ptr.I=%d ptr.S=%s\n", ptr.I, ptr.S)
// ptr.I=42 ptr.S=Some Other String...
}
Here's the complete program in a playground.
If you're interested in a more complex example, say with
slices and maps, then, take a look at creasty/defaultse
From https://golang.org/doc/effective_go.html#composite_literals:
Sometimes the zero value isn't good enough and an initializing constructor is necessary, as in this example derived from package os.
func NewFile(fd int, name string) *File {
if fd < 0 {
return nil
}
f := new(File)
f.fd = fd
f.name = name
f.dirinfo = nil
f.nepipe = 0
return f
}
What about making something like this:
// Card is the structure we work with
type Card struct {
Html js.Value
DefaultText string `default:"html"` // this only works with strings
}
// Init is the main function that initiate the structure, and return it
func (c Card) Init() Card {
c.Html = Document.Call("createElement", "div")
return c
}
Then call it as:
c := new(Card).Init()
I found this thread very helpful and educational. The other answers already provide good guidance, but I wanted to summarize my takeaways with an easy to reference (i.e. copy-paste) approach:
package main
import (
"fmt"
)
// Define an interface that is exported by your package.
type Foo interface {
GetValue() string // A function that'll return the value initialized with a default.
SetValue(v string) // A function that can update the default value.
}
// Define a struct type that is not exported by your package.
type foo struct {
value string
}
// A factory method to initialize an instance of `foo`,
// the unexported struct, with a default value.
func NewFoo() Foo {
return &foo{
value: "I am the DEFAULT value.",
}
}
// Implementation of the interface's `GetValue`
// for struct `foo`.
func (f *foo) GetValue() string {
return f.value
}
// Implementation of the interface's `SetValue`
// for struct `foo`.
func (f *foo) SetValue(v string) {
f.value = v
}
func main() {
f := NewFoo()
fmt.Printf("value: `%s`\n", f.GetValue())
f.SetValue("I am the UPDATED value.")
fmt.Printf("value: `%s`\n", f.GetValue())
}
One way to do that is:
// declare a type
type A struct {
Filed1 string
Field2 map[string]interface{}
}
So whenever you need a new variable of your custom defined type just call the NewA function also you can parameterise the function to optionally assign the values to the struct fields
func NewA() *A {
return &A{
Filed1: "",
Field2: make(map[string]interface{}),
}
}
for set default values in Go structs we use anonymous struct:
Person := struct {
name string
age int
city string
}{
name: "Peter",
age: 21,
city: "Noida",
}
fmt.Println(Person)
Structs
An easy way to make this program better is to use a struct. A struct is a type which contains named fields. For example we could represent a Circle like this:
type Circle struct {
x float64
y float64
r float64
}
The type keyword introduces a new type. It's followed by the name of the type (Circle), the keyword struct to indicate that we are defining a struct type and a list of fields inside of curly braces. Each field has a name and a type. Like with functions we can collapse fields that have the same type:
type Circle struct {
x, y, r float64
}
Initialization
We can create an instance of our new Circle type in a variety of ways:
var c Circle
Like with other data types, this will create a local Circle variable that is by default set to zero. For a struct zero means each of the fields is set to their corresponding zero value (0 for ints, 0.0 for floats, "" for strings, nil for pointers, …) We can also use the new function:
c := new(Circle)
This allocates memory for all the fields, sets each of them to their zero value and returns a pointer. (*Circle) More often we want to give each of the fields a value. We can do this in two ways. Like this:
c := Circle{x: 0, y: 0, r: 5}
Or we can leave off the field names if we know the order they were defined:
c := Circle{0, 0, 5}
type Config struct {
AWSRegion string `default:"us-west-2"`
}

Nested properties for structs with unknown property names?

I'm using JSON to get some values into a variable from an external source.
I have a type like this that json.Unmarshal puts values into:
type Frame struct {
Type string
Value map[string]interface{}
}
var data Frame
After unmarshal, I can access a the type by: data.Type
but if I try doing something like:
if data.Type == "image" {
fmt.Printf("%s\n", data.Value.Imagedata)
}
The compiler complains about no such value data.Value.Imagedata.
So my question is, how do I reference properties in Go that I know will be there depending on some condition?
Doing this works:
type Image struct {
Filename string
}
type Frame struct {
Type string
Value map[string]interface{}
}
But that isn't very flexible as I will be receiving different Values.
json.Unmarshal will do its best to place the data where it best aligns with your type. Technically your first example will work, but you are trying to access the Value field with dot notation, even though you declared it to be a map:
This should give you some form of output:
if data.Type == 'image'{
fmt.Printf("%v\n", data.Value["Imagedata"])
}
… considering that "Imagedata" was a key in the JSON.
You have the option of defining the type as deeply as you want or expect the structure to be, or using an interface{} and then doing type assertions on the values. With the Value field being a map, you would always access the keys like Value[key], and the value of that map entry is an interface{} which you could type assert like Value[key].(float64).
As for doing more explicit structures, I have found that you could either break up the objects into their own types, or define it nested in one place:
Nested (with anonymous struct)
type Frame struct {
Type string
Value struct {
Imagedata string `json:"image_data"`
}
}
Seperate structs
type Frame struct {
Type string
Value value
}
type value struct {
Imagedata string `json:"image_data"`
}
I'm still learning Go myself, so this the extent of my current understanding :-).

Resources