I have a "key1:value1; key2:value2" like string (string with key:value pattern concated by ;).
Now I wish to parse this string to a Go struct:
type CustomStruct struct {
KeyName1 string `name:"key1" somevalidation:"xxx"`
KeyName2 int `name:"key2" somevalidation:"yyy"`
}
In the above example, the struct tag defines the name of the key in the string and can provide some validation for its corresponding value (it can set a default value if validation fails). For instance, KeyName2 is an int value, so I wish the somevalidation can check whether the KeyName2 satisfy, let's say, greater than 30 and less equal than 100.
And in another senario, I can define another struct CustomStruct2 for string like key3:value3; key4:value4;
How can I archive this kind of requirement efficiently and elegantly?
I'll assume that you can parse the data to a map[string]interface{}.
Use the reflect package to set the fields. Here's the basic function:
// set sets fields in struct pointed to by pv to values in data.
func set(pv interface{}, data map[string]interface{}) {
// pv is assumed to be pointer to a struct
s := reflect.ValueOf(pv).Elem()
// Loop through fields
t := s.Type()
for i := 0; i < t.NumField(); i++ {
// Set field if there's a data value for the field.
f := t.Field(i)
if d, ok := data[f.Tag.Get("name")]; ok {
s.Field(i).Set(reflect.ValueOf(d))
}
}
}
This code assumes that the values in the data map are assignable to the corresponding field in the struct and that the first argument is a pointer to a struct. The code will panic if these assumptions are not true. You can protect against this by checking types and assignability using the reflect package.
playground example
Related
I'd like to iterate over the fields in a struct and prompt for string values to string fields, doing this recursively for fields that are pointers to structs.
Currently this is what I've tried, but I get an error when trying to set this value in the pointer's string field.
package main
import (
"fmt"
"reflect"
)
type Table struct {
PK *Field
}
type Field struct {
Name string
}
func main() {
PopulateStruct(&Table{})
}
func PopulateStruct(a interface{}) interface {} {
typeOf := reflect.TypeOf(a)
valueOf := reflect.ValueOf(a)
for i := 0; i < typeOf.Elem().NumField(); i++ {
switch typeOf.Elem().Field(i).Type.Kind() {
case reflect.String:
fmt.Print(typeOf.Elem().Field(i).Name)
var s string
fmt.Scanf("%s", &s)
valueOf.Elem().Field(i).SetString(s)
case reflect.Ptr:
ptr := reflect.New(valueOf.Elem().Field(i).Type())
PopulateStruct(ptr.Elem().Interface())
valueOf.Elem().Field(i).Set(ptr)
}
}
}
Expecting the return value to include an initialised struct with the pointers string field set.
Getting an error when setting the pointer's string field.
panic: reflect: call of reflect.Value.Field on zero Value
I dropped your code as-is into the Go Playground and it doesn't build because PopulateStruct is declared as returning interface{} but does not actually return anything. Removing the declared return type produces the panic you mention.
This is because at entry to the outer PopulateStruct call, you have a valid pointer, pointing to a zero-valued Table. A zero-valued Table has one element: a nil pointer in it of type *Field. Your loop therefore runs once and finds a reflect.Ptr, as you expected. Adding more diagnostic print messages helps see what's happening:
fmt.Printf("PopulateStruct: I have typeOf=%v, valueOf=%v\n", typeOf, valueOf)
for i := 0; i < typeOf.Elem().NumField(); i++ {
switch typeOf.Elem().Field(i).Type.Kind() {
// ... snipped some code ...
case reflect.Ptr:
ptr := reflect.New(valueOf.Elem().Field(i).Type())
fmt.Println("after allocating ptr, we have:", ptr.Type(), ptr,
"but its Elem is:", ptr.Elem().Type(), ptr.Elem())
This prints:
PopulateStruct: I have typeOf=*main.Table, valueOf=&{<nil>}
after allocating ptr, we have: **main.Field 0x40c138 but its Elem is: *main.Field <nil>
Given the way PopulateStruct itself is constructed, we must actually allocate a real Field instance now, before calling PopulateStruct. We can do this with:
p2 := ptr.Elem()
ptr.Elem().Set(reflect.New(p2.Type().Elem()))
(code borrowed from json.Unmarshal). Now we can fill in this Field, which has one field named Name of type String.
The overall strategy here is not that great, in my opinion: filling-in probably should take a generic pointer, not specifically a pointer-to-struct pointer. You can then emulate the indirect function in the json unmarshaller. However, the addition of these two lines—creating the target object and making the allocated pointer point to it—suffices to make your existing code run.
(Alternatively, you could just create and return a whole instance from scratch, in which case all you need is the type—but I'm assuming you have a pattern in which only some fields are nil.)
Here's the complete Go Playground example. I made a few other changes as there's nothing to scan from when using the playground.
I would like to iterate over the fields of a struct after unmarshalling a JSON object into it and check for the fields whose value was not set (i.e. are empty).
I can get the value of each field and compare that to the reflect.Zero value for the corresponding type
json.Unmarshal([]byte(str), &res)
s := reflect.ValueOf(&res).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
v := reflect.ValueOf(f.Interface())
if (reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())) {
....
But the problem, of course, is that this will not work well for bool or int values.
If a bool field is set to false in the JSON or an int field is set to 0, they will be equal to the zero value of their type. The aforementioned check will consider the fields to be uninitialized, even though they actually have a value set.
I know one way around this is to use pointers, but I just don't see how that would be possible in this case as I'm working with reflect.Value types, not the actual struct.
As you've mentioned, you could use pointers.
The json package can handle unmarshalling values into pointers for you. You've not included the json payload you are trying to unmarshal, or the struct you are unmarshalling into, so I've made up an example.
// json
{
"foo": true,
"number_of_foos": 14
}
// go struct
type Foo struct {
Present bool `json:"foo"`
Num int `json:"number_of_foos"`
}
Here if the keys foo or number_of_foos is missing, then as you've correctly observed, the zero value (false/ 0) will be used. In general the best advice is to make use of the zero value. Create structures so that zero values of false are useful, rather than a pain.
This is not always possible, so changing the types of the fields in the Foo struct to be pointers will allow you to check the 3 cases you are after.
Present
Present and zero
Missing
here is the same struct with pointers:
// go struct
type Foo struct {
Present *bool `json:"foo"`
Num *int `json:"number_of_foos"`
}
Now you can check for presence of the value with fooStruct.Present != nil and if that condition holds, you can assume that the value in the field is the one you wanted.
There is no need to use the reflect package.
Another way of doing the same is by implementing json.Unmarshaler.
type MaybeInt struct {
Present bool
Value int
}
func (i *MaybeInt) UnmarshalJSON(bs []byte) error {
if e := json.Unmarshal(bs, &i.Value); e != nil {
return e
}
i.Present = true
return nil
}
You can then use MaybeInt in your top-level structure:
type Top struct {
N MaybeInt `json:"n"`
M MaybeInt `json:"m"`
}
func main() {
t := Top{}
if e := json.Unmarshal([]byte(` { "n": 4930 } `), &t); e != nil {
panic(e)
}
fmt.Println(t.N, t.M)
}
See it working on the playground
Try using the golang validator package. The validator package offers a required attribute that might do the required job for your need. The official documentation for required attribute states:
This validates that the value is not the data types default zero value. For numbers ensures value is not zero. For strings ensures value is not "". For slices, maps, pointers, interfaces, channels and functions ensures the value is not nil.
The example illustrating the same can be seen at: https://github.com/go-playground/validator/blob/v9/_examples/struct-level/main.go.
Hope this solves your requirement.
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
}
I'm not very clear about what this code snippet behaves.
func show(i interface{}) {
switch t := i.(type) {
case *Person:
t := reflect.TypeOf(i) //what t contains?
v := reflect.ValueOf(i) //what v contains?
tag := t.Elem().Field(0).Tag
name := v.Elem().Field(0).String()
}
}
What is the difference between the type and value in reflection?
reflect.TypeOf() returns a reflect.Type and reflect.ValueOf() returns a reflect.Value. A reflect.Type allows you to query information that is tied to all variables with the same type while reflect.Value allows you to query information and preform operations on data of an arbitrary type.
Also reflect.ValueOf(i).Type() is equivalent to reflect.TypeOf(i).
In the example above, you are using the reflect.Type to get the "tag" of the first field in the Person struct. You start out with the Type for *Person. To get the type information of Person, you used t.Elem(). Then you pulled the tag information about the first field using .Field(0).Tag. The actual value you passed, i, does not matter because the Tag of the first field is part of the type.
You used reflect.Value to get a string representation of the first field of the value i. First you used v.Elem() to get a Value for the struct pointed to by i, then accessed the first Field's data (.Field(0)), and finally turned that data into a string (.String()).
How could I get an output of struct, sorted by fields?
type T struct {
B int
A int
}
t := &T{B: 2, A: 1}
doSomething(t)
fmt.Println(t) // &{1 2} --> Sorted by fields
A struct is an ordered collection of fields. The fmt package uses reflection to get the fields and values of a struct value, and generates output in the order in which they were defined.
So the simplest solution would be to declare your type where you already have your fields arranged in alphabetical order:
type T struct {
A int
B int
}
If you can't modify the order of fields (e.g. memory layout is important), you can implement the Stringer interface by specifying a String() method for your struct type:
func (t T) String() string {
return fmt.Sprintf("{%d %d}", t.A, t.B)
}
The fmt package checks if the passed value implements Stringer, and if it does, calls its String() method to generate the output.
Cons of this solution is that this is not flexible (e.g. if you add a new field, you have to update the String() method too), also you have to do it for every struct type you want it to work (and you can't define methods for types defined in other packages).
The completely flexible solution can use reflection. You can get the names of fields, sort them by name, and then iterate over the sorted names and get the field values (by name).
Pros of this solution is that this works for any struct, and it keeps working without modification even if you add or remove fields from your structs. It also works for fields of any type, not just for int fields.
Here is an example how to do it (try it on the Go Playground):
func printFields(st interface{}) string {
t := reflect.TypeOf(st)
names := make([]string, t.NumField())
for i := range names {
names[i] = t.Field(i).Name
}
sort.Strings(names)
v := reflect.ValueOf(st)
buf := &bytes.Buffer{}
buf.WriteString("{")
for i, name := range names {
val := v.FieldByName(name)
if !val.CanInterface() {
continue
}
if i > 0 {
buf.WriteString(" ")
}
fmt.Fprintf(buf, "%v", val.Interface())
}
buf.WriteString("}")
return buf.String()
}
Make T implement the Stringer interface (see package fmt) and do either print A orb B first.
BTW. This is a stupid idea.