Skip to content

Commit 98f7564

Browse files
committed
add custom handling of NullFields to marshaling
1 parent 4b7b22a commit 98f7564

File tree

2 files changed

+58
-3
lines changed

2 files changed

+58
-3
lines changed

response.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ func visitModelNode(model interface{}, included *map[string]*Node,
233233

234234
modelValue := value.Elem()
235235
modelType := value.Type().Elem()
236+
nullFields := modelValue.FieldByName("NullFields").Interface().(*[]string)
236237

237238
for i := 0; i < modelValue.NumField(); i++ {
238239
fieldValue := modelValue.Field(i)
@@ -348,15 +349,15 @@ func visitModelNode(model interface{}, included *map[string]*Node,
348349
} else if fieldValue.Type() == reflect.TypeOf(new(time.Time)) {
349350
// A time pointer may be nil
350351
if fieldValue.IsNil() {
351-
if omitEmpty {
352+
if omitEmpty && !stringInSlice(nullFields, structField.Name) {
352353
continue
353354
}
354355

355356
node.Attributes[args[1]] = nil
356357
} else {
357358
tm := fieldValue.Interface().(*time.Time)
358359

359-
if tm.IsZero() && omitEmpty {
360+
if tm.IsZero() && omitEmpty && !stringInSlice(nullFields, structField.Name) {
360361
continue
361362
}
362363

@@ -373,7 +374,7 @@ func visitModelNode(model interface{}, included *map[string]*Node,
373374
emptyValue := reflect.Zero(fieldValue.Type())
374375

375376
// See if we need to omit this field
376-
if omitEmpty && reflect.DeepEqual(fieldValue.Interface(), emptyValue.Interface()) {
377+
if omitEmpty && reflect.DeepEqual(fieldValue.Interface(), emptyValue.Interface()) && !stringInSlice(nullFields, structField.Name) {
377378
continue
378379
}
379380

@@ -648,3 +649,16 @@ func convertToSliceInterface(i *interface{}) ([]interface{}, error) {
648649
}
649650
return response, nil
650651
}
652+
653+
func stringInSlice(s *[]string, v string) bool {
654+
if s == nil {
655+
return false
656+
}
657+
658+
for _, field := range *s {
659+
if field == v {
660+
return true
661+
}
662+
}
663+
return false
664+
}

response_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,47 @@ func TestWithOmitsEmptyAnnotationOnAttribute(t *testing.T) {
518518
}
519519
}
520520

521+
func TestOmitsEmptyAnnotationAndNullFieldOnAttribute(t *testing.T) {
522+
type Author struct{}
523+
524+
type Book struct {
525+
ID int `jsonapi:"primary,books"`
526+
Name string `jsonapi:"attr,name"`
527+
Author *Author `jsonapi:"attr,author,omitempty"`
528+
PublishedAt *time.Time `jsonapi:"attr,published_at,omitempty"`
529+
530+
NullFields *[]string `jsonapi:"-"`
531+
}
532+
533+
book := &Book{
534+
ID: 123,
535+
Name: "The Confidence Man",
536+
PublishedAt: nil,
537+
538+
NullFields: &[]string{"PublishedAt"},
539+
}
540+
541+
out := bytes.NewBuffer(nil)
542+
if err := MarshalPayload(out, book); err != nil {
543+
t.Fatal(err)
544+
}
545+
546+
var jsonData map[string]interface{}
547+
if err := json.Unmarshal(out.Bytes(), &jsonData); err != nil {
548+
t.Fatal(err)
549+
}
550+
551+
payload := jsonData["data"].(map[string]interface{})
552+
attributes := payload["attributes"].(map[string]interface{})
553+
if _, ok := attributes["published_at"]; !ok {
554+
t.Fatal("Was expecting the data.attributes.published_at to have NOT been omitted")
555+
}
556+
557+
if _, ok := attributes["author"]; ok {
558+
t.Fatal("Was expecting the data.attributes.author to have been omitted")
559+
}
560+
}
561+
521562
func TestMarshalIDPtr(t *testing.T) {
522563
id, make, model := "123e4567-e89b-12d3-a456-426655440000", "Ford", "Mustang"
523564
car := &Car{

0 commit comments

Comments
 (0)