Skip to content
This repository was archived by the owner on Sep 4, 2025. It is now read-only.

Commit 417d4eb

Browse files
authored
Merge pull request #6 from shwoodard/shwoodard-slemgrim-pull-99
Use jsonapi struct tags for nested attrs
2 parents 8b7e0bc + 3c8221b commit 417d4eb

File tree

3 files changed

+53
-89
lines changed

3 files changed

+53
-89
lines changed

models_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -165,14 +165,14 @@ type Company struct {
165165
}
166166

167167
type Team struct {
168-
Name string `json:"name"`
169-
Leader *Employee `json:"leader"`
170-
Members []Employee `json:"members"`
168+
Name string `jsonapi:"attr,name"`
169+
Leader *Employee `jsonapi:"attr,leader"`
170+
Members []Employee `jsonapi:"attr,members"`
171171
}
172172

173173
type Employee struct {
174-
Firstname string `json:"firstname"`
175-
Surname string `json:"surname"`
176-
Age int `json:"age"`
177-
HiredAt *time.Time `json:"hired-at,iso8601"`
174+
Firstname string `jsonapi:"attr,firstname"`
175+
Surname string `jsonapi:"attr,surname"`
176+
Age int `jsonapi:"attr,age"`
177+
HiredAt *time.Time `jsonapi:"attr,hired-at,iso8601"`
178178
}

request.go

Lines changed: 31 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
)
1414

1515
const (
16-
unsuportedStructTagMsg = "Unsupported jsonapi tag annotation, %s"
16+
unsupportedStructTagMsg = "Unsupported jsonapi tag annotation, %s"
1717
)
1818

1919
var (
@@ -147,7 +147,7 @@ func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node)
147147
}()
148148

149149
modelValue := model.Elem()
150-
modelType := model.Type().Elem()
150+
modelType := modelValue.Type()
151151

152152
var er error
153153

@@ -217,39 +217,8 @@ func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node)
217217

218218
// Convert the numeric float to one of the supported ID numeric types
219219
// (int[8,16,32,64] or uint[8,16,32,64])
220-
var idValue reflect.Value
221-
switch kind {
222-
case reflect.Int:
223-
n := int(floatValue)
224-
idValue = reflect.ValueOf(&n)
225-
case reflect.Int8:
226-
n := int8(floatValue)
227-
idValue = reflect.ValueOf(&n)
228-
case reflect.Int16:
229-
n := int16(floatValue)
230-
idValue = reflect.ValueOf(&n)
231-
case reflect.Int32:
232-
n := int32(floatValue)
233-
idValue = reflect.ValueOf(&n)
234-
case reflect.Int64:
235-
n := int64(floatValue)
236-
idValue = reflect.ValueOf(&n)
237-
case reflect.Uint:
238-
n := uint(floatValue)
239-
idValue = reflect.ValueOf(&n)
240-
case reflect.Uint8:
241-
n := uint8(floatValue)
242-
idValue = reflect.ValueOf(&n)
243-
case reflect.Uint16:
244-
n := uint16(floatValue)
245-
idValue = reflect.ValueOf(&n)
246-
case reflect.Uint32:
247-
n := uint32(floatValue)
248-
idValue = reflect.ValueOf(&n)
249-
case reflect.Uint64:
250-
n := uint64(floatValue)
251-
idValue = reflect.ValueOf(&n)
252-
default:
220+
idValue, err := handleNumeric(floatValue, fieldType.Type, fieldValue)
221+
if err != nil {
253222
// We had a JSON float (numeric), but our field was not one of the
254223
// allowed numeric types
255224
er = ErrBadJSONAPIID
@@ -358,7 +327,7 @@ func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node)
358327
}
359328

360329
} else {
361-
er = fmt.Errorf(unsuportedStructTagMsg, annotation)
330+
er = fmt.Errorf(unsupportedStructTagMsg, annotation)
362331
}
363332
}
364333

@@ -395,33 +364,33 @@ func unmarshalAttribute(
395364

396365
// Handle field of type []string
397366
if fieldValue.Type() == reflect.TypeOf([]string{}) {
398-
value, err = handleStringSlice(attribute, args, fieldType, fieldValue)
367+
value, err = handleStringSlice(attribute)
399368
return
400369
}
401370

402371
// Handle field of type time.Time
403372
if fieldValue.Type() == reflect.TypeOf(time.Time{}) ||
404373
fieldValue.Type() == reflect.TypeOf(new(time.Time)) {
405-
value, err = handleTime(attribute, args, fieldType, fieldValue)
374+
value, err = handleTime(attribute, args, fieldValue)
406375
return
407376
}
408377

409378
// Handle field of type struct
410379
if fieldValue.Type().Kind() == reflect.Struct {
411-
value, err = handleStruct(attribute, args, fieldType, fieldValue)
380+
value, err = handleStruct(attribute, fieldValue)
412381
return
413382
}
414383

415384
// Handle field containing slice of structs
416385
if fieldValue.Type().Kind() == reflect.Slice &&
417386
reflect.TypeOf(fieldValue.Interface()).Elem().Kind() == reflect.Struct {
418-
value, err = handleStructSlice(attribute, args, fieldType, fieldValue)
387+
value, err = handleStructSlice(attribute, fieldValue)
419388
return
420389
}
421390

422391
// JSON value was a float (numeric)
423392
if value.Kind() == reflect.Float64 {
424-
value, err = handleNumeric(attribute, args, fieldType, fieldValue)
393+
value, err = handleNumeric(attribute, fieldType, fieldValue)
425394
return
426395
}
427396

@@ -440,11 +409,7 @@ func unmarshalAttribute(
440409
return
441410
}
442411

443-
func handleStringSlice(
444-
attribute interface{},
445-
args []string,
446-
fieldType reflect.Type,
447-
fieldValue reflect.Value) (reflect.Value, error) {
412+
func handleStringSlice(attribute interface{}) (reflect.Value, error) {
448413
v := reflect.ValueOf(attribute)
449414
values := make([]string, v.Len())
450415
for i := 0; i < v.Len(); i++ {
@@ -454,11 +419,7 @@ func handleStringSlice(
454419
return reflect.ValueOf(values), nil
455420
}
456421

457-
func handleTime(
458-
attribute interface{},
459-
args []string,
460-
fieldType reflect.Type,
461-
fieldValue reflect.Value) (reflect.Value, error) {
422+
func handleTime(attribute interface{}, args []string, fieldValue reflect.Value) (reflect.Value, error) {
462423
var isIso8601 bool
463424
v := reflect.ValueOf(attribute)
464425

@@ -507,7 +468,6 @@ func handleTime(
507468

508469
func handleNumeric(
509470
attribute interface{},
510-
args []string,
511471
fieldType reflect.Type,
512472
fieldValue reflect.Value) (reflect.Value, error) {
513473
v := reflect.ValueOf(attribute)
@@ -584,12 +544,12 @@ func handlePointer(
584544
concreteVal = reflect.ValueOf(&cVal)
585545
case map[string]interface{}:
586546
var err error
587-
concreteVal, err = handleStruct(attribute, args, fieldType, fieldValue)
547+
concreteVal, err = handleStruct(attribute, fieldValue)
588548
if err != nil {
589549
return reflect.Value{}, newErrUnsupportedPtrType(
590550
reflect.ValueOf(attribute), fieldType, structField)
591551
}
592-
return concreteVal.Elem(), err
552+
return concreteVal, err
593553
default:
594554
return reflect.Value{}, newErrUnsupportedPtrType(
595555
reflect.ValueOf(attribute), fieldType, structField)
@@ -605,37 +565,42 @@ func handlePointer(
605565

606566
func handleStruct(
607567
attribute interface{},
608-
args []string,
609-
fieldType reflect.Type,
610568
fieldValue reflect.Value) (reflect.Value, error) {
611-
model := reflect.New(fieldValue.Type())
612569

613570
data, err := json.Marshal(attribute)
614571
if err != nil {
615-
return model, err
572+
return reflect.Value{}, err
616573
}
617574

618-
err = json.Unmarshal(data, model.Interface())
575+
node := new(Node)
576+
if err := json.Unmarshal(data, &node.Attributes); err != nil {
577+
return reflect.Value{}, err
578+
}
619579

620-
if err != nil {
621-
return model, err
580+
var model reflect.Value
581+
if fieldValue.Kind() == reflect.Ptr {
582+
model = reflect.New(fieldValue.Type().Elem())
583+
} else {
584+
model = reflect.New(fieldValue.Type())
622585
}
623586

624-
return model, err
587+
if err := unmarshalNode(node, model, nil); err != nil {
588+
return reflect.Value{}, err
589+
}
590+
591+
592+
return model, nil
625593
}
626594

627595
func handleStructSlice(
628596
attribute interface{},
629-
args []string,
630-
fieldType reflect.Type,
631597
fieldValue reflect.Value) (reflect.Value, error) {
632598
models := reflect.New(fieldValue.Type()).Elem()
633599
dataMap := reflect.ValueOf(attribute).Interface().([]interface{})
634600
for _, data := range dataMap {
635601
model := reflect.New(fieldValue.Type().Elem()).Elem()
636-
modelType := model.Type()
637602

638-
value, err := handleStruct(data, []string{}, modelType, model)
603+
value, err := handleStruct(data, model)
639604

640605
if err != nil {
641606
continue

request_test.go

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,8 +1013,8 @@ func sampleSerializedEmbeddedTestModel() *Blog {
10131013

10141014
func TestUnmarshalNestedStructPtr(t *testing.T) {
10151015
type Director struct {
1016-
Firstname string `json:"firstname"`
1017-
Surname string `json:"surname"`
1016+
Firstname string `jsonapi:"attr,firstname"`
1017+
Surname string `jsonapi:"attr,surname"`
10181018
}
10191019
type Movie struct {
10201020
ID string `jsonapi:"primary,movies"`
@@ -1058,7 +1058,6 @@ func TestUnmarshalNestedStructPtr(t *testing.T) {
10581058
}
10591059

10601060
func TestUnmarshalNestedStruct(t *testing.T) {
1061-
10621061
boss := map[string]interface{}{
10631062
"firstname": "Hubert",
10641063
"surname": "Farnsworth",
@@ -1074,22 +1073,22 @@ func TestUnmarshalNestedStruct(t *testing.T) {
10741073
"name": "Planet Express",
10751074
"boss": boss,
10761075
"founded-at": "2016-08-17T08:27:12Z",
1077-
"teams": []Team{
1078-
Team{
1079-
Name: "Dev",
1080-
Members: []Employee{
1081-
Employee{Firstname: "Sean"},
1082-
Employee{Firstname: "Iz"},
1076+
"teams": []map[string]interface{}{
1077+
map[string]interface{}{
1078+
"name": "Dev",
1079+
"members": []map[string]interface{}{
1080+
map[string]interface{}{"firstname": "Sean"},
1081+
map[string]interface{}{"firstname": "Iz"},
10831082
},
1084-
Leader: &Employee{Firstname: "Iz"},
1083+
"leader": map[string]interface{}{"firstname": "Iz"},
10851084
},
1086-
Team{
1087-
Name: "DxE",
1088-
Members: []Employee{
1089-
Employee{Firstname: "Akshay"},
1090-
Employee{Firstname: "Peri"},
1085+
map[string]interface{}{
1086+
"name": "DxE",
1087+
"members": []map[string]interface{}{
1088+
map[string]interface{}{"firstname": "Akshay"},
1089+
map[string]interface{}{"firstname": "Peri"},
10911090
},
1092-
Leader: &Employee{Firstname: "Peri"},
1091+
"leader": map[string]interface{}{"firstname": "Peri"},
10931092
},
10941093
},
10951094
},

0 commit comments

Comments
 (0)