From e800f8241dd8e2f39d83c141a4152a577aa0e458 Mon Sep 17 00:00:00 2001 From: meredith_burgess Date: Mon, 21 Oct 2019 15:34:02 -0600 Subject: [PATCH] Add method to parse underlying type into object --- decode/decode_test.go | 19 +++++++++++++++++++ decode/decoder.go | 26 +++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/decode/decode_test.go b/decode/decode_test.go index 4c2642f..093b5a7 100644 --- a/decode/decode_test.go +++ b/decode/decode_test.go @@ -9,6 +9,7 @@ import ( "fmt" "io/ioutil" "testing" + "time" . "github.com/smartystreets/goconvey/convey" "github.com/weberr13/go-decode/decode" @@ -84,6 +85,11 @@ type LivesInStruct struct { LivesIn *RequiredBasicTypes } +type TimedStruct struct { + Name string + UpdateTime *time.Time +} + func MyTestFactory(kind string) (interface{}, error) { fm := map[string]func() interface{}{ "record": NewRecord, @@ -449,4 +455,17 @@ func TestDecodeNestedObject(t *testing.T) { _, err := decode.UnmarshalJSONInto([]byte(b), &PetOwner{}, SchemaPathFactory) So(err, ShouldNotBeNil) }) + Convey("Test parsing and decoding with underlying type", t, func() { + b := `{ "name": "john", "updateTime": "2019-10-21T14:56:28.292468-06:00" }` + i, err := decode.UnmarshalJSONInto([]byte(b), &TimedStruct{}, nil) + So(err, ShouldBeNil) + expTime, _ := time.Parse(time.RFC3339, "2019-10-21T14:56:28.292468-06:00") + So(i.(*TimedStruct), ShouldResemble, &TimedStruct{Name: "john", UpdateTime: &expTime}) + + }) + Convey("Test parsing and decoding with incorrect underlying type", t, func() { + b := `{ "name": "john", "updateTime": "string" }` + _, err := decode.UnmarshalJSONInto([]byte(b), &TimedStruct{}, nil) + So(err, ShouldNotBeNil) + }) } diff --git a/decode/decoder.go b/decode/decoder.go index 57d1cb2..8158a95 100644 --- a/decode/decoder.go +++ b/decode/decoder.go @@ -159,7 +159,11 @@ func DecodeInto(m map[string]interface{}, o interface{}, pf PathFactory) (interf nV := reflect.New(ft) if !vV.Type().ConvertibleTo(ft) { - return nil, fmt.Errorf("cannot convert value (%v) to field '%s' type\n", v, fldName) + err := parseAndSetField(fldName, field, nV, vV) + if err != nil { + return nil, err + } + continue } nV.Elem().Set(vV.Convert(ft)) field.Set(nV.Elem().Addr()) @@ -340,3 +344,23 @@ func UnmarshalJSONInto(b []byte, o interface{}, pf PathFactory) (interface{}, er } return DecodeInto(m, o, pf) } + +func parseAndSetField(path string, field, newField, val reflect.Value) error { + unmarshaler, ok := newField.Interface().(json.Unmarshaler) + if ok { + // marshal val back to []byte since it was converted to some underlying type (int/string) + valBytes, err := json.Marshal(val.Interface()); + if err != nil { + return fmt.Errorf("Cannot convert value to []byte when attempting to set '%s': %s", path, err) + } + // unmarshal valBytes back into newField object via the unmarshaler + err = unmarshaler.UnmarshalJSON(valBytes) + if err != nil { + return fmt.Errorf("Cannot unmarshal byte values for field '%s': %s", path, err) + } + // set field to the value unmarshaled into newField + field.Set(newField) + return nil + } + return fmt.Errorf("cannot convert value (%v) to field '%s' type", val, path) +} \ No newline at end of file