diff --git a/jsonapi_test.go b/jsonapi_test.go index f42c5b9..d59f4ec 100644 --- a/jsonapi_test.go +++ b/jsonapi_test.go @@ -173,6 +173,7 @@ var ( articleRelatedInvalidEmptyRelationshipBody = `{"data":{"id":"1","type":"articles","attributes":{"title":"A"},"relationships":{"author":{}}}}` articleRelatedInvalidEmptyDataBody = `{"data":{"id":"1","type":"articles","attributes":{"title":"A"},"relationships":{"author":{"data":{}}}}}` articleRelatedNoOmitEmptyBody = `{"data":{"id":"1","type":"articles","attributes":{"title":"A"},"relationships":{"author":{"data":null},"comments":{"data":[]}}}}` + articleRelatedLiteralNullData = `{"data":{"id":"1","type":"articles","attributes":{"title":"A"},"relationships":{"author":{"data":null},"comments":{"data":null}}}}` articleRelatedAuthorBody = `{"data":{"id":"1","type":"articles","attributes":{"title":"A"},"relationships":{"author":{"data":{"id":"1","type":"author"},"links":{"self":"http://example.com/articles/1/relationships/author","related":"http://example.com/articles/1/author"}}}}}` articleRelatedAuthorTwiceBody = `{"data":[{"id":"1","type":"articles","attributes":{"title":"A"},"relationships":{"author":{"data":{"id":"1","type":"author"}}}},{"id":"2","type":"articles","attributes":{"title":"B"},"relationships":{"author":{"data":{"id":"1","type":"author"}}}}]}` articleRelatedAuthorTwiceWithIncludeBody = `{"data":[{"id":"1","type":"articles","attributes":{"title":"A"},"relationships":{"author":{"data":{"id":"1","type":"author"}}}},{"id":"2","type":"articles","attributes":{"title":"B"},"relationships":{"author":{"data":{"id":"1","type":"author"}}}}],"included":[{"id":"1","type":"author","attributes":{"name":"A"}}]}` diff --git a/reflect.go b/reflect.go index 4bd4c2b..3b0a5e6 100644 --- a/reflect.go +++ b/reflect.go @@ -47,3 +47,10 @@ func setFieldValue(fv reflect.Value, v any) { } fv.Set(vv) } + +func canBeNil(fv reflect.Value) bool { + return fv.Kind() == reflect.Interface || + fv.Kind() == reflect.Map || + fv.Kind() == reflect.Pointer || + fv.Kind() == reflect.Slice +} diff --git a/unmarshal.go b/unmarshal.go index cb7798b..c8c8e30 100644 --- a/unmarshal.go +++ b/unmarshal.go @@ -293,6 +293,9 @@ func (ro *resourceObject) unmarshalFields(v any, rv reflect.Value, rt reflect.Ty } if !relDocument.hasMany && relDocument.isEmpty() { // ensure struct field is nil for data:null cases only (we want empty slice for data:[]) + if canBeNil(fv) { + fv.Set(reflect.Zero(ft.Type)) + } continue } diff --git a/unmarshal_test.go b/unmarshal_test.go index 524365b..67ae5a4 100644 --- a/unmarshal_test.go +++ b/unmarshal_test.go @@ -330,6 +330,19 @@ func TestUnmarshal(t *testing.T) { }, expect: &ArticleRelated{}, expectError: ErrEmptyDataObject, + }, { + description: "*ArticleRelated with nil relationships data", + given: articleRelatedLiteralNullData, + do: func(body []byte) (any, error) { + a := ArticleRelated{ + Author: &Author{ID: "100"}, + Comments: []*Comment{{ID: "200"}}, + } + err := Unmarshal(body, &a) + return &a, err + }, + expect: &ArticleRelated{ID: "1", Title: "A", Author: nil, Comments: nil}, + expectError: nil, }, { description: "*ArticleRelated.Author", given: articleRelatedAuthorBody,