diff --git a/request.go b/request.go index a7bb0b1..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) @@ -591,7 +604,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..cdd096c 100644 --- a/request_test.go +++ b/request_test.go @@ -1306,3 +1306,75 @@ 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,omitempty"` + Raw json.RawMessage `jsonapi:"attr,raw,omitempty"` +} + +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) + } +} + +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)) + } + } +}