From 4309b1d2fac939de31503933806f793bcf71ad84 Mon Sep 17 00:00:00 2001 From: Igor Zibarev Date: Fri, 27 Apr 2018 17:09:10 +0300 Subject: [PATCH] Support node id unmarshalling for custom types --- request.go | 24 ++++++++++- request_test.go | 106 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 2 deletions(-) diff --git a/request.go b/request.go index fe29706..5f6d5ae 100644 --- a/request.go +++ b/request.go @@ -542,9 +542,29 @@ func fullNode(n *Node, included *map[string]*Node) *Node { // assign will take the value specified and assign it to the field; if // field is expecting a ptr assign will assign a ptr. func assign(field, value reflect.Value) { - if field.Kind() == reflect.Ptr { + if value.Type().AssignableTo(field.Type()) { field.Set(value) + return + } + + indirectValue := reflect.Indirect(value) + if indirectValue.Type().AssignableTo(field.Type()) { + field.Set(indirectValue) + return + } + + // Conversion required. + + if field.Kind() == reflect.Ptr { + if value.Kind() == reflect.Ptr { + field.Set(value.Convert(field.Type())) + } else { + // Because field is zero value, we cannot simply field.Elem().Set(). + v := reflect.New(field.Type().Elem()) + v.Elem().Set(value.Convert(field.Type().Elem())) + field.Set(v) + } } else { - field.Set(reflect.Indirect(value)) + field.Set(reflect.Indirect(value).Convert(field.Type())) } } diff --git a/request_test.go b/request_test.go index 2206449..facf971 100644 --- a/request_test.go +++ b/request_test.go @@ -703,6 +703,112 @@ func TestManyPayload_withLinks(t *testing.T) { } } +func TestUnmarshalPayloadIDTypeOfString(t *testing.T) { + t.Run("Unmarshal string to value type", func(t *testing.T) { + data := map[string]interface{}{ + "data": map[string]interface{}{ + "type": "books", + "id": "978-3-16-148410-0", + "attributes": map[string]interface{}{ + "title": "Gesammelte Werke in deutscher Sprache", + }, + }, + } + b, err := json.Marshal(data) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + type ( + IBSN string + + Book struct { + ID IBSN `jsonapi:"primary,books"` + Title string `jsonapi:"attr,title"` + } + ) + + book := &Book{} + if err := UnmarshalPayload(bytes.NewReader(b), book); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expected := IBSN("978-3-16-148410-0") + if book.ID != expected { + t.Fatalf("Expected book id to be %v but got %v", expected, book.ID) + } + }) + + t.Run("Unmarshal string to ptr type", func(t *testing.T) { + data := map[string]interface{}{ + "data": map[string]interface{}{ + "type": "books", + "id": "978-3-16-148410-0", + "attributes": map[string]interface{}{ + "title": "Gesammelte Werke in deutscher Sprache", + }, + }, + } + b, err := json.Marshal(data) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + type ( + IBSN string + + Book struct { + ID *IBSN `jsonapi:"primary,books"` + Title string `jsonapi:"attr,title"` + } + ) + + book := &Book{} + if err := UnmarshalPayload(bytes.NewReader(b), book); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + expected := IBSN("978-3-16-148410-0") + if !reflect.DeepEqual(book.ID, &expected) { + t.Fatalf("Expected book id to be %v but got %v", &expected, book.ID) + } + }) + + t.Run("Unmarshal nil to ptr type", func(t *testing.T) { + data := map[string]interface{}{ + "data": map[string]interface{}{ + "type": "books", + "id": nil, + "attributes": map[string]interface{}{ + "title": "Gesammelte Werke in deutscher Sprache", + }, + }, + } + b, err := json.Marshal(data) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + type ( + IBSN string + + Book struct { + ID *IBSN `jsonapi:"primary,books"` + Title string `jsonapi:"attr,title"` + } + ) + + book := &Book{} + if err := UnmarshalPayload(bytes.NewReader(b), book); err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + if book.ID != nil { + t.Fatalf("Expected book id to be %v but got %v", nil, book.ID) + } + }) +} + func samplePayloadWithoutIncluded() map[string]interface{} { return map[string]interface{}{ "data": map[string]interface{}{