Skip to content

Commit 055f87a

Browse files
authored
Unmarshal json null relationship data to nil (#65)
When unmarshaling relationship data that is the json literal null to an interface, map, pointer, or slice, set the value to nil instead of the current behavior, which is to assume the value is already nil and do nothing. If unmarshaling to any other type, continue to do nothing.
1 parent 0d0bed5 commit 055f87a

File tree

4 files changed

+24
-0
lines changed

4 files changed

+24
-0
lines changed

jsonapi_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ var (
173173
articleRelatedInvalidEmptyRelationshipBody = `{"data":{"id":"1","type":"articles","attributes":{"title":"A"},"relationships":{"author":{}}}}`
174174
articleRelatedInvalidEmptyDataBody = `{"data":{"id":"1","type":"articles","attributes":{"title":"A"},"relationships":{"author":{"data":{}}}}}`
175175
articleRelatedNoOmitEmptyBody = `{"data":{"id":"1","type":"articles","attributes":{"title":"A"},"relationships":{"author":{"data":null},"comments":{"data":[]}}}}`
176+
articleRelatedLiteralNullData = `{"data":{"id":"1","type":"articles","attributes":{"title":"A"},"relationships":{"author":{"data":null},"comments":{"data":null}}}}`
176177
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"}}}}}`
177178
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"}}}}]}`
178179
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"}}]}`

reflect.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,10 @@ func setFieldValue(fv reflect.Value, v any) {
4747
}
4848
fv.Set(vv)
4949
}
50+
51+
func canBeNil(fv reflect.Value) bool {
52+
return fv.Kind() == reflect.Interface ||
53+
fv.Kind() == reflect.Map ||
54+
fv.Kind() == reflect.Pointer ||
55+
fv.Kind() == reflect.Slice
56+
}

unmarshal.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,9 @@ func (ro *resourceObject) unmarshalFields(v any, rv reflect.Value, rt reflect.Ty
293293
}
294294
if !relDocument.hasMany && relDocument.isEmpty() {
295295
// ensure struct field is nil for data:null cases only (we want empty slice for data:[])
296+
if canBeNil(fv) {
297+
fv.Set(reflect.Zero(ft.Type))
298+
}
296299
continue
297300
}
298301

unmarshal_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,19 @@ func TestUnmarshal(t *testing.T) {
330330
},
331331
expect: &ArticleRelated{},
332332
expectError: ErrEmptyDataObject,
333+
}, {
334+
description: "*ArticleRelated with nil relationships data",
335+
given: articleRelatedLiteralNullData,
336+
do: func(body []byte) (any, error) {
337+
a := ArticleRelated{
338+
Author: &Author{ID: "100"},
339+
Comments: []*Comment{{ID: "200"}},
340+
}
341+
err := Unmarshal(body, &a)
342+
return &a, err
343+
},
344+
expect: &ArticleRelated{ID: "1", Title: "A", Author: nil, Comments: nil},
345+
expectError: nil,
333346
}, {
334347
description: "*ArticleRelated.Author",
335348
given: articleRelatedAuthorBody,

0 commit comments

Comments
 (0)