diff --git a/.gitignore b/.gitignore index 19b1e1c..b7e5cb2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /examples/examples +.idea/ diff --git a/response.go b/response.go index b44e4e9..035c6f4 100644 --- a/response.go +++ b/response.go @@ -51,17 +51,16 @@ var ( // Many Example: you could pass it, w, your http.ResponseWriter, and, models, a // slice of Blog struct instance pointers to be written to the response body: // -// func ListBlogs(w http.ResponseWriter, r *http.Request) { -// blogs := []*Blog{} +// func ListBlogs(w http.ResponseWriter, r *http.Request) { +// blogs := []*Blog{} // -// w.Header().Set("Content-Type", jsonapi.MediaType) -// w.WriteHeader(http.StatusOK) +// w.Header().Set("Content-Type", jsonapi.MediaType) +// w.WriteHeader(http.StatusOK) // -// if err := jsonapi.MarshalPayload(w, blogs); err != nil { -// http.Error(w, err.Error(), http.StatusInternalServerError) +// if err := jsonapi.MarshalPayload(w, blogs); err != nil { +// http.Error(w, err.Error(), http.StatusInternalServerError) +// } // } -// } -// func MarshalPayload(w io.Writer, models interface{}) error { payload, err := Marshal(models) if err != nil { @@ -514,17 +513,21 @@ func appendIncluded(m *map[string]*Node, nodes ...*Node) { func nodeMapValues(m *map[string]*Node) []*Node { mp := *m - nodes := make([]*Node, len(mp)) + nodes := make([]*Node, 0) - i := 0 for _, n := range mp { - nodes[i] = n - i++ + if !isNodeEmpty(n) { + nodes = append(nodes, n) + } } return nodes } +func isNodeEmpty(n *Node) bool { + return (n.Attributes == nil || len(n.Attributes) == 0) && (n.Relationships == nil || len(n.Relationships) == 0) +} + func convertToSliceInterface(i *interface{}) ([]interface{}, error) { vals := reflect.ValueOf(*i) if vals.Kind() != reflect.Slice { diff --git a/response_test.go b/response_test.go index b1d5967..aeb33a5 100644 --- a/response_test.go +++ b/response_test.go @@ -957,6 +957,61 @@ func TestMarshal_InvalidIntefaceArgument(t *testing.T) { } } +func TestMarshal_EmptyAttributesNotIncluded(t *testing.T) { + type Primary struct { + ID string `jsonapi:"primary,primary"` + Attr string `jsonapi:"attr,attr,omitempty"` + Secondary *Primary `jsonapi:"relation,secondary,omitempty"` + Secondaries []*Primary `jsonapi:"relation,secondaries,omitempty"` + } + + p := &Primary{ + ID: "1", + Attr: "a", + Secondary: &Primary{ + ID: "2", + }, + Secondaries: []*Primary{ + {ID: "3"}, + }, + } + + t.Run("Single payload", func(t *testing.T) { + out := bytes.NewBuffer(nil) + if err := MarshalPayload(out, p); err != nil { + t.Fatal(err) + } + + var jsonData map[string]interface{} + if err := json.Unmarshal(out.Bytes(), &jsonData); err != nil { + t.Fatal(err) + } + + _, ok := jsonData["included"].([]interface{}) + if ok { + t.Fatal("Was expecting included to be empty") + } + }) + + t.Run("Many payload", func(t *testing.T) { + + out := bytes.NewBuffer(nil) + if err := MarshalPayload(out, []*Primary{p}); err != nil { + t.Fatal(err) + } + + var jsonData map[string]interface{} + if err := json.Unmarshal(out.Bytes(), &jsonData); err != nil { + t.Fatal(err) + } + + _, ok := jsonData["included"].([]interface{}) + if ok { + t.Fatal("Was expecting included to be empty") + } + }) +} + func testBlog() *Blog { return &Blog{ ID: 5,