From 0af5cf6d148a0131980d20694ea95e0696a3239f Mon Sep 17 00:00:00 2001 From: Michele Finotto Date: Tue, 5 Mar 2019 07:34:33 +0000 Subject: [PATCH 1/2] Improve handleStruct to use struct's own Unmarshaler if it is available --- request.go | 14 +++++++++++++- request_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/request.go b/request.go index a7bb0b1..8d6c879 100644 --- a/request.go +++ b/request.go @@ -591,7 +591,19 @@ func handlePointer( func handleStruct( attribute interface{}, fieldValue reflect.Value) (reflect.Value, error) { - + if fieldValue.CanAddr() { + interf := fieldValue.Addr().Interface() + if _, ok := interf.(json.Unmarshaler); ok { + var tmp []byte + tmp, err := json.Marshal(attribute) + if err == nil { + err = json.Unmarshal(tmp, interf) + if err == nil { + return reflect.ValueOf(interf), nil + } + } + } + } data, err := json.Marshal(attribute) if err != nil { return reflect.Value{}, err diff --git a/request_test.go b/request_test.go index 3326598..832c03d 100644 --- a/request_test.go +++ b/request_test.go @@ -1306,3 +1306,44 @@ func TestUnmarshalNestedStructSlice(t *testing.T) { out.Teams[0].Members[0].Firstname) } } + +type MyCustomAttribute struct { + Field string +} + +func (mca MyCustomAttribute) MarshalJSON() ([]byte, error) { + return json.Marshal(mca.Field) +} +func (mca *MyCustomAttribute) UnmarshalJSON(bts []byte) error { + return json.Unmarshal(bts, &mca.Field) +} + +type StructForTest struct { + ID string `jsonapi:"primary,tests"` + Custom MyCustomAttribute `jsonapi:"attr,custom"` +} + +func TestUnmarshalWithCustomType(t *testing.T) { + sft := &StructForTest{ + ID: "my-id", + Custom: MyCustomAttribute{ + Field: "a-string", + }, + } + buf := new(bytes.Buffer) + err := MarshalPayload(buf, sft) + if err != nil { + t.Fatal(err) + } + newSft := &StructForTest{} + err = UnmarshalPayload(buf, newSft) + + if err != nil { + t.Fatal(err) + } + + if sft.Custom.Field != newSft.Custom.Field { + t.Fatalf("Custom type wasn't properly unmarshalled: Expected to have `%s` but got `%s`", + sft.Custom.Field, newSft.Custom.Field) + } +} From fa5f73018bc41cb49a15e10e07dedd040cab70ef Mon Sep 17 00:00:00 2001 From: Michele Finotto Date: Thu, 14 Mar 2019 11:31:32 +0000 Subject: [PATCH 2/2] Fix unmarshalAttribute to support unmarshaling into a json.RawMessage --- request.go | 13 +++++++++++++ request_test.go | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/request.go b/request.go index 8d6c879..037de88 100644 --- a/request.go +++ b/request.go @@ -393,6 +393,11 @@ func unmarshalAttribute( return } + // Handle field of type json.RawMessage + if fieldValue.Type() == reflect.TypeOf(json.RawMessage{}) { + value, err = handleJSONRawMessage(attribute) + } + // Handle field of type time.Time if fieldValue.Type() == reflect.TypeOf(time.Time{}) || fieldValue.Type() == reflect.TypeOf(new(time.Time)) { @@ -444,6 +449,14 @@ func handleStringSlice(attribute interface{}) (reflect.Value, error) { return reflect.ValueOf(values), nil } +func handleJSONRawMessage(attribute interface{}) (reflect.Value, error) { + tmp, err := json.Marshal(attribute) + if err != nil { + return reflect.Value{}, err + } + return reflect.ValueOf(json.RawMessage(tmp)), nil +} + func handleTime(attribute interface{}, args []string, fieldValue reflect.Value) (reflect.Value, error) { var isIso8601 bool v := reflect.ValueOf(attribute) diff --git a/request_test.go b/request_test.go index 832c03d..cdd096c 100644 --- a/request_test.go +++ b/request_test.go @@ -1320,7 +1320,8 @@ func (mca *MyCustomAttribute) UnmarshalJSON(bts []byte) error { type StructForTest struct { ID string `jsonapi:"primary,tests"` - Custom MyCustomAttribute `jsonapi:"attr,custom"` + Custom MyCustomAttribute `jsonapi:"attr,custom,omitempty"` + Raw json.RawMessage `jsonapi:"attr,raw,omitempty"` } func TestUnmarshalWithCustomType(t *testing.T) { @@ -1347,3 +1348,33 @@ func TestUnmarshalWithCustomType(t *testing.T) { sft.Custom.Field, newSft.Custom.Field) } } + +func TestUnmarshalWithJSONRawMessage(t *testing.T) { + tests := [][]byte{ + []byte(`{"really":{"deep":true},"test":"toast"}`), + []byte(`"just a string"`), + []byte(`123`), + } + for _, v := range tests { + sft := &StructForTest{ + ID: "my-id", + Raw: v, + } + buf := new(bytes.Buffer) + err := MarshalPayload(buf, sft) + if err != nil { + t.Fatal(err) + } + newSft := &StructForTest{} + err = UnmarshalPayload(buf, newSft) + + if err != nil { + t.Fatal(err) + } + + if bytes.Compare(sft.Raw, newSft.Raw) != 0 { + t.Fatalf("json.RawMessage wasn't properly unmarshalled: Expected to have `%s` but got `%s`", + string(sft.Raw), string(newSft.Raw)) + } + } +}