Basics working
diff --git a/README.md b/README.md new file mode 100644 index 0000000..185aa0b --- /dev/null +++ b/README.md
@@ -0,0 +1,10 @@ +# mapstructure + +mapstructure is a Go library for converting generic map values to structures +and vice versa, while providing helpful error handling. + +This library is most useful when decoding values from some data stream (JSON, +Gob, etc.) where you don't _quite_ know the structure of the underlying data +until you read a part of it. You can therefore read a `map[string]interface{}` +and use this library to decode it into the proper underlying native Go +structure.
diff --git a/mapstructure.go b/mapstructure.go new file mode 100644 index 0000000..eaf505d --- /dev/null +++ b/mapstructure.go
@@ -0,0 +1,62 @@ +package mapstructure + +import ( + "errors" + "reflect" + "strings" +) + +// MapToStruct takes a map and uses reflection to convert it into the +// given Go native structure. val must be a pointer to a struct. +func MapToStruct(m map[string]interface{}, rawVal interface{}) error { + val := reflect.ValueOf(rawVal).Elem() + if !val.CanAddr() { + return errors.New("val must be addressable (a pointer)") + } + + if val.Kind() != reflect.Struct { + return errors.New("val must be an addressable struct") + } + + valType := val.Type() + + for i := 0; i < valType.NumField(); i++ { + fieldType := valType.Field(i) + fieldName := fieldType.Name + + rawMapVal, ok := m[fieldName] + if !ok { + // Do a slower search by iterating over each key and + // doing case-insensitive search. + for mK, mV := range m { + if strings.EqualFold(mK, fieldName) { + rawMapVal = mV + break + } + } + + if rawMapVal == nil { + // There was no matching key in the map for the value in + // the struct. Just ignore. + continue + } + } + + field := val.Field(i) + if !field.IsValid() { + // This should never happen + panic("field is not valid") + } + + mapVal := reflect.ValueOf(rawMapVal) + if !mapVal.IsValid() { + // This should never happen because we got the value out + // of the map. + panic("map value is not valid") + } + + field.Set(mapVal) + } + + return nil +}
diff --git a/mapstructure_test.go b/mapstructure_test.go new file mode 100644 index 0000000..593ba06 --- /dev/null +++ b/mapstructure_test.go
@@ -0,0 +1,38 @@ +package mapstructure + +import "testing" + +type Basic struct { + Vstring string + Vint int + Vbool bool +} + +func TestBasicTypes(t *testing.T) { + t.Parallel() + + input := map[string]interface{}{ + "vstring": "foo", + "vint": 42, + "vbool": true, + } + + var result Basic + err := MapToStruct(input, &result) + if err != nil { + t.Errorf("got an err: %s", err.Error()) + t.FailNow() + } + + if result.Vstring != "foo" { + t.Errorf("vstring value should be 'foo': %#v", result.Vstring) + } + + if result.Vint != 42 { + t.Errorf("vint value should be 42: %#v", result.Vint) + } + + if result.Vbool != true { + t.Errorf("vbool value should be true: %#v", result.Vbool) + } +}