Skip to content

Commit a898266

Browse files
author
Sam Woodard
committed
wip on #21 basic serialization. todo, deserialize embedded
1 parent ec72d59 commit a898266

File tree

3 files changed

+95
-16
lines changed

3 files changed

+95
-16
lines changed

node.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,35 @@ type Node struct {
2020
Relationships map[string]interface{} `json:"relationships,omitempty"`
2121
}
2222

23+
// Extend applies the attirbutes on the rhs to the callee
24+
func (n *Node) Extend(node *Node) {
25+
for attr, val := range node.Attributes {
26+
n.AddAttriute(attr, val)
27+
}
28+
29+
for rel, val := range node.Relationships {
30+
n.AddRelationship(rel, val)
31+
}
32+
}
33+
34+
// AddRelationship adds a relationship to the Node
35+
func (n *Node) AddRelationship(name string, val interface{}) {
36+
if n.Relationships == nil {
37+
n.Relationships = make(map[string]interface{})
38+
}
39+
40+
n.Relationships[name] = val
41+
}
42+
43+
// AddAttriute adds an attribute to the Node
44+
func (n *Node) AddAttriute(name string, val interface{}) {
45+
if n.Attributes == nil {
46+
n.Attributes = make(map[string]interface{})
47+
}
48+
49+
n.Attributes[name] = val
50+
}
51+
2352
type RelationshipOneNode struct {
2453
Data *Node `json:"data"`
2554
Links *map[string]string `json:"links,omitempty"`

response.go

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func MarshalOnePayload(w io.Writer, model interface{}) error {
4242
func MarshalOne(model interface{}) (*OnePayload, error) {
4343
included := make(map[string]*Node)
4444

45-
rootNode, err := visitModelNode(model, &included, true)
45+
rootNode, err := visitModelNode(reflect.ValueOf(model), &included, true)
4646
if err != nil {
4747
return nil, err
4848
}
@@ -99,7 +99,7 @@ func MarshalMany(models []interface{}) (*ManyPayload, error) {
9999
for i := 0; i < len(models); i++ {
100100
model := models[i]
101101

102-
node, err := visitModelNode(model, &included, true)
102+
node, err := visitModelNode(reflect.ValueOf(model), &included, true)
103103
if err != nil {
104104
return nil, err
105105
}
@@ -131,7 +131,7 @@ func MarshalMany(models []interface{}) (*ManyPayload, error) {
131131
//
132132
// model interface{} should be a pointer to a struct.
133133
func MarshalOnePayloadEmbedded(w io.Writer, model interface{}) error {
134-
rootNode, err := visitModelNode(model, nil, false)
134+
rootNode, err := visitModelNode(reflect.ValueOf(model), nil, false)
135135
if err != nil {
136136
return err
137137
}
@@ -145,15 +145,28 @@ func MarshalOnePayloadEmbedded(w io.Writer, model interface{}) error {
145145
return nil
146146
}
147147

148-
func visitModelNode(model interface{}, included *map[string]*Node, sideload bool) (*Node, error) {
148+
func visitModelNode(model reflect.Value, included *map[string]*Node, sideload bool) (*Node, error) {
149149
node := new(Node)
150150

151151
var er error
152152

153-
modelValue := reflect.ValueOf(model).Elem()
153+
modelValue := model.Elem()
154154

155155
for i := 0; i < modelValue.NumField(); i++ {
156156
structField := modelValue.Type().Field(i)
157+
158+
if structField.Anonymous {
159+
if modelValue.Field(i).IsNil() {
160+
continue
161+
}
162+
if n, err := visitModelNode(modelValue.Field(i), included, sideload); err == nil {
163+
node.Extend(n)
164+
} else {
165+
err = err
166+
break
167+
}
168+
}
169+
157170
tag := structField.Tag.Get("jsonapi")
158171
if tag == "" {
159172
continue
@@ -252,10 +265,6 @@ func visitModelNode(model interface{}, included *map[string]*Node, sideload bool
252265
continue
253266
}
254267

255-
if node.Relationships == nil {
256-
node.Relationships = make(map[string]interface{})
257-
}
258-
259268
if isSlice {
260269
relationship, err := visitModelNodeRelationships(args[1], fieldValue, included, sideload)
261270

@@ -269,22 +278,22 @@ func visitModelNode(model interface{}, included *map[string]*Node, sideload bool
269278
shallowNodes = append(shallowNodes, toShallowNode(n))
270279
}
271280

272-
node.Relationships[args[1]] = &RelationshipManyNode{Data: shallowNodes}
281+
node.AddRelationship(args[1], &RelationshipManyNode{Data: shallowNodes})
273282
} else {
274-
node.Relationships[args[1]] = relationship
283+
node.AddRelationship(args[1], relationship)
275284
}
276285
} else {
277286
er = err
278287
break
279288
}
280289
} else {
281-
relationship, err := visitModelNode(fieldValue.Interface(), included, sideload)
290+
relationship, err := visitModelNode(fieldValue, included, sideload)
282291
if err == nil {
283292
if sideload {
284293
appendIncluded(included, relationship)
285-
node.Relationships[args[1]] = &RelationshipOneNode{Data: toShallowNode(relationship)}
294+
node.AddRelationship(args[1], &RelationshipOneNode{Data: toShallowNode(relationship)})
286295
} else {
287-
node.Relationships[args[1]] = &RelationshipOneNode{Data: relationship}
296+
node.AddRelationship(args[1], &RelationshipOneNode{Data: relationship})
288297
}
289298
} else {
290299
er = err
@@ -321,7 +330,7 @@ func visitModelNodeRelationships(relationName string, models reflect.Value, incl
321330

322331
for i := 0; i < models.Len(); i++ {
323332
n := models.Index(i).Interface()
324-
node, err := visitModelNode(n, included, sideload)
333+
node, err := visitModelNode(reflect.ValueOf(n), included, sideload)
325334
if err != nil {
326335
return nil, err
327336
}

response_test.go

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package jsonapi
33
import (
44
"bytes"
55
"encoding/json"
6+
"fmt"
67
"testing"
78
"time"
89
)
@@ -19,7 +20,7 @@ type Blog struct {
1920
}
2021

2122
type Post struct {
22-
Blog
23+
*Rating
2324
Id int `jsonapi:"primary,posts"`
2425
BlogId int `jsonapi:"attr,blog_id"`
2526
ClientId string `jsonapi:"client-id"`
@@ -30,12 +31,48 @@ type Post struct {
3031
}
3132

3233
type Comment struct {
34+
*Rating
3335
Id int `jsonapi:"primary,comments"`
3436
ClientId string `jsonapi:"client-id"`
3537
PostId int `jsonapi:"attr,post_id"`
3638
Body string `jsonapi:"attr,body"`
3739
}
3840

41+
type Rating struct {
42+
Score int `jsonapi:"attr,score"`
43+
Comment string `jsonapi:"attr,comment"`
44+
}
45+
46+
func TestEmbedded(t *testing.T) {
47+
blog := testBlog()
48+
buf := bytes.NewBuffer(nil)
49+
50+
if err := MarshalOnePayloadEmbedded(buf, blog); err != nil {
51+
t.Fatal(err)
52+
}
53+
54+
payload := new(OnePayload)
55+
if err := json.NewDecoder(buf).Decode(payload); err != nil {
56+
t.Fatal(err)
57+
}
58+
59+
posts := payload.Data.Relationships["posts"].(map[string]interface{})["data"].([]interface{})
60+
firstPostData := posts[0].(map[string]interface{})
61+
firstPostAttrs := posts[0].(map[string]interface{})["attributes"].(map[string]interface{})
62+
63+
if val, exists := firstPostAttrs["score"]; !exists || val.(float64) != 5.0 {
64+
t.Fatalf("Expected emedded score, 5, got %d", val)
65+
}
66+
67+
expected := "Bad ass post!"
68+
if val, exists := firstPostAttrs["comment"]; !exists || val.(string) != expected {
69+
t.Fatalf("Expected emebedded rating comment to be, %s, was %s.", expected, val)
70+
}
71+
}
72+
73+
func TestEmbedded_nil(t *testing.T) {
74+
}
75+
3976
func TestHasPrimaryAnnotation(t *testing.T) {
4077
testModel := &Blog{
4178
Id: 5,
@@ -243,6 +280,10 @@ func testBlog() *Blog {
243280
CreatedAt: time.Now(),
244281
Posts: []*Post{
245282
&Post{
283+
Rating: &Rating{
284+
Score: 5,
285+
Comment: "Bad ass post!",
286+
},
246287
Id: 1,
247288
Title: "Foo",
248289
Body: "Bar",

0 commit comments

Comments
 (0)