Created
March 16, 2012 10:58
-
-
Save farnoy/2049589 to your computer and use it in GitHub Desktop.
Reflection assignment for struct fields in Go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package fields | |
| import ( | |
| "fmt" | |
| "reflect" | |
| ) | |
| // Provides a shortcut and prettier syntax for map definitions. | |
| type Assigns map[string]reflect.Value | |
| // Returns an array of field names for given interface | |
| // that must be a struct. | |
| func CollectFieldNames(object interface{}) (names []string) { | |
| typ := reflect.TypeOf(object) | |
| if typ.Kind() == reflect.Ptr { typ = typ.Elem() } | |
| if typ.Kind() != reflect.Struct { | |
| fmt.Printf("%v type can't have attributes inspected", typ.Kind()) | |
| return | |
| } | |
| for i := 0; i < typ.NumField(); i++ { | |
| f := typ.Field(i) | |
| if !f.Anonymous { | |
| names = append(names, f.Name) | |
| } | |
| } | |
| return | |
| } | |
| // Assign values to object's fields by matching | |
| // fields by names given in data and taken from object. | |
| // | |
| // The object must be a reference (addressable). | |
| // | |
| // If the object given is not a struct, warns and quits. | |
| // | |
| // When the above condition is satisfied, then for each pair in the given map: | |
| // | |
| // If there is no field with wanted name | |
| // <=> Nothing happens | |
| // If there is a field with wanted name: | |
| // * And the type of the value to assign is different from the type of matched field | |
| // <=> Nothing happens | |
| // * And the type of the value to assign is the same as the type of matched field | |
| // <=> Value is assigned to that field | |
| // | |
| // Returns an error when unmarshalling can't be done for object | |
| func Unmarshal(data Assigns, object interface{}) error { | |
| v := reflect.ValueOf(object) | |
| if v.Kind() == reflect.Ptr { | |
| v = v.Elem() | |
| } else { | |
| return fmt.Errorf("object for unmarshalling has to be addressable") | |
| } | |
| if v.Kind() != reflect.Struct { | |
| return fmt.Errorf("type can't have attributes inspected: %v", v.Kind()) | |
| } | |
| for name, val := range data { | |
| if field := v.FieldByName(name); field.IsValid() && val.Kind() == field.Kind() { | |
| field.Set(val) | |
| } | |
| } | |
| return nil | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package fields | |
| import ( | |
| "testing" | |
| "reflect" | |
| ) | |
| type Container struct { | |
| Key string | |
| Value int | |
| } | |
| func ExampleUnmarshal() { | |
| c := Container{} | |
| Unmarshal(Assigns{"Key": reflect.ValueOf("test")}, &c) // remember to use a reference here | |
| print(c.Key) // "test" | |
| } | |
| func TestUnmarshalWithGoodSettings(t *testing.T) { | |
| c := Container{} | |
| Unmarshal(Assigns{"Key": reflect.ValueOf("test")}, &c) | |
| if c.Key != "test" { t.Error("Expected Unmarshal to change field accordingly") } | |
| } | |
| func TestUnmarshalWithWrongSettings(t *testing.T) { | |
| c := Container{} | |
| Unmarshal(Assigns{"Key": reflect.ValueOf(5)}, &c) | |
| if c.Key != "" { t.Error("Expected Unmarshal to not change field when types do not match") } | |
| } | |
| func TestUnmarshalWithWrongFields(t *testing.T) { | |
| c := Container{} | |
| Unmarshal(Assigns{"DoesNotExist": reflect.ValueOf(nil)}, &c) | |
| if c.Key != "" || c.Value != 0 { t.Error("Expected Unmarshal to not change anything when no field matches") } | |
| } | |
| func TestUnmarshalRaisingExceptionWithUnaddressable(t *testing.T) { | |
| c := Container{} | |
| if err := Unmarshal(Assigns{"DoesNotExist": reflect.ValueOf(nil)}, c); err.Error() != "object for unmarshalling has to be addressable" { | |
| t.Error("Expected Unmarshal to catch unaddressable objects") | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "fmt" | |
| "goref/fields" | |
| "reflect" | |
| ) | |
| type Container struct { | |
| Name string | |
| Value int | |
| } | |
| func main() { | |
| cont := Container{} | |
| fmt.Println("Container before", cont) | |
| fmt.Println("\nFields:") | |
| for _, name := range fields.CollectFieldNames(cont) { | |
| fmt.Println("*", name) | |
| } | |
| m := fields.Assigns{ | |
| "Name": reflect.ValueOf("Kuba"), | |
| "Value": reflect.ValueOf(16), | |
| "UNREAL": reflect.ValueOf(1e2), | |
| } | |
| fields.Unmarshal(m, &cont) | |
| fmt.Println("\nContainer after", cont) | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Container before { 0} | |
| Fields: | |
| * Name | |
| * Value | |
| Container after {Kuba 16} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment