diff --git a/jsonhal.go b/jsonhal.go index 4a787cc..2a06e60 100644 --- a/jsonhal.go +++ b/jsonhal.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "reflect" + "time" "github.com/mitchellh/mapstructure" ) @@ -44,6 +45,7 @@ type Embedder interface { type Hal struct { Links map[string]*Link `json:"_links,omitempty"` Embedded map[string]Embedded `json:"_embedded,omitempty"` + decoder *mapstructure.Decoder } // SetLink sets a link (self, next, etc). Title argument is optional @@ -105,13 +107,46 @@ func (h *Hal) CountEmbedded(name string) (int, error) { return reflect.ValueOf(interface{}(e)).Len(), nil } +// decodeHook is used to support datatypes that mapstructure does not support native +func (h *Hal) decodeHook(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + + // only if target datatype is time.Time and if source datatype is string + if t == reflect.TypeOf(time.Time{}) && f == reflect.TypeOf("") { + return time.Parse(time.RFC3339, data.(string)) + } + + //everything else would not be handled for now + return data, nil +} + // DecodeEmbedded decodes embedded objects into a struct -func (h *Hal) DecodeEmbedded(name string, result interface{}) error { +func (h *Hal) DecodeEmbedded(name string, result interface{}) (err error) { + var dec *mapstructure.Decoder + defer func() { + if r := recover(); r != nil { + err = r.(error) + + } + }() + e, err := h.GetEmbedded(name) if err != nil { - return err + panic(err) + } + //setup a new decoder if not already present + if h.decoder == nil { + dec, err = mapstructure.NewDecoder(&mapstructure.DecoderConfig{Result: result, DecodeHook: h.decodeHook}) + if err != nil { + panic(err) + } + h.decoder = dec + } + + err = h.decoder.Decode(e) + if err != nil { + panic(err) } - return mapstructure.Decode(interface{}(e), result) + return nil } // DeleteEmbedded removes an embedded resource named name if it is found diff --git a/jsonhal_test.go b/jsonhal_test.go index 3e53982..530ed18 100644 --- a/jsonhal_test.go +++ b/jsonhal_test.go @@ -6,8 +6,10 @@ import ( "log" "reflect" "testing" + "time" "github.com/RichardKnop/jsonhal" + "github.com/stretchr/testify/assert" ) @@ -21,8 +23,9 @@ type HelloWorld struct { // Foobar is a simple test struct type Foobar struct { jsonhal.Hal - ID uint `json:"id"` - Name string `json:"name"` + ID uint `json:"id"` + Name string `json:"name"` + Date time.Time `json:"date"` } // Qux is a simple test struct @@ -67,7 +70,8 @@ var expectedJSON3 = []byte(`{ } }, "id": 1, - "name": "Foo bar 1" + "name": "Foo bar 1", + "date":"2017-09-12T08:45:20Z" } }, "id": 1, @@ -89,7 +93,8 @@ var expectedJSON4 = []byte(`{ } }, "id": 1, - "name": "Foo bar 1" + "name": "Foo bar 1", + "date":"2017-09-12T08:45:20Z" }, { "_links": { @@ -98,7 +103,8 @@ var expectedJSON4 = []byte(`{ } }, "id": 2, - "name": "Foo bar 2" + "name": "Foo bar 2", + "date":"2017-09-12T08:45:20Z" } ] }, @@ -121,7 +127,8 @@ var expectedJSON5 = []byte(`{ } }, "id": 1, - "name": "Foo bar 1" + "name": "Foo bar 1", + "date":"2017-09-12T08:45:20Z" }, { "_links": { @@ -130,7 +137,8 @@ var expectedJSON5 = []byte(`{ } }, "id": 2, - "name": "Foo bar 2" + "name": "Foo bar 2", + "date":"2017-09-12T08:45:20Z" } ], "quxes": [ @@ -204,6 +212,7 @@ func TestHal(t *testing.T) { assert.Equal(t, expected.String(), string(actual)) // Let's add more links and a single embedded resource + date, _ := time.Parse(time.RFC3339, "2017-09-12T08:45:20Z") helloWorld = &HelloWorld{ID: 1, Name: "Hello World"} helloWorld.SetLink( "self", // name @@ -220,7 +229,7 @@ func TestHal(t *testing.T) { "/v1/hello/world?offset=0&limit=2", // href "", // title ) - foobar = &Foobar{ID: 1, Name: "Foo bar 1"} + foobar = &Foobar{ID: 1, Name: "Foo bar 1", Date: date} foobar.SetLink("self", "/v1/foo/bar/1", "") helloWorld.SetEmbedded("foobar", jsonhal.Embedded(foobar)) @@ -254,6 +263,7 @@ func TestHal(t *testing.T) { }, ID: 1, Name: "Foo bar 1", + Date: date, }, { Hal: jsonhal.Hal{ @@ -263,6 +273,7 @@ func TestHal(t *testing.T) { }, ID: 2, Name: "Foo bar 2", + Date: date, }, } helloWorld.SetEmbedded("foobars", jsonhal.Embedded(foobars)) @@ -297,6 +308,7 @@ func TestHal(t *testing.T) { }, ID: 1, Name: "Foo bar 1", + Date: date, }, { Hal: jsonhal.Hal{ @@ -306,6 +318,7 @@ func TestHal(t *testing.T) { }, ID: 2, Name: "Foo bar 2", + Date: date, }, } helloWorld.SetEmbedded("foobars", jsonhal.Embedded(foobars)) @@ -491,6 +504,7 @@ func TestUnmarshalingAndDecodeEmbedded(t *testing.T) { assert.NoError(t, hw.DecodeEmbedded("foobar", f)) assert.Equal(t, uint(1), f.ID) assert.Equal(t, "Foo bar 1", f.Name) + assert.Equal(t, "2017-09-12T08:45:20Z", f.Date.Format(time.RFC3339)) // Slice of embedded objects