Skip to content

Use jsonapi struct tags for nested attrs #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions models_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,14 @@ type Company struct {
}

type Team struct {
Name string `json:"name"`
Leader *Employee `json:"leader"`
Members []Employee `json:"members"`
Name string `jsonapi:"attr,name"`
Leader *Employee `jsonapi:"attr,leader"`
Members []Employee `jsonapi:"attr,members"`
}

type Employee struct {
Firstname string `json:"firstname"`
Surname string `json:"surname"`
Age int `json:"age"`
HiredAt *time.Time `json:"hired-at,iso8601"`
Firstname string `jsonapi:"attr,firstname"`
Surname string `jsonapi:"attr,surname"`
Age int `jsonapi:"attr,age"`
HiredAt *time.Time `jsonapi:"attr,hired-at,iso8601"`
}
97 changes: 31 additions & 66 deletions request.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
)

const (
unsuportedStructTagMsg = "Unsupported jsonapi tag annotation, %s"
unsupportedStructTagMsg = "Unsupported jsonapi tag annotation, %s"
)

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

modelValue := model.Elem()
modelType := model.Type().Elem()
modelType := modelValue.Type()

var er error

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

// Convert the numeric float to one of the supported ID numeric types
// (int[8,16,32,64] or uint[8,16,32,64])
var idValue reflect.Value
switch kind {
case reflect.Int:
n := int(floatValue)
idValue = reflect.ValueOf(&n)
case reflect.Int8:
n := int8(floatValue)
idValue = reflect.ValueOf(&n)
case reflect.Int16:
n := int16(floatValue)
idValue = reflect.ValueOf(&n)
case reflect.Int32:
n := int32(floatValue)
idValue = reflect.ValueOf(&n)
case reflect.Int64:
n := int64(floatValue)
idValue = reflect.ValueOf(&n)
case reflect.Uint:
n := uint(floatValue)
idValue = reflect.ValueOf(&n)
case reflect.Uint8:
n := uint8(floatValue)
idValue = reflect.ValueOf(&n)
case reflect.Uint16:
n := uint16(floatValue)
idValue = reflect.ValueOf(&n)
case reflect.Uint32:
n := uint32(floatValue)
idValue = reflect.ValueOf(&n)
case reflect.Uint64:
n := uint64(floatValue)
idValue = reflect.ValueOf(&n)
default:
idValue, err := handleNumeric(floatValue, fieldType.Type, fieldValue)
if err != nil {
// We had a JSON float (numeric), but our field was not one of the
// allowed numeric types
er = ErrBadJSONAPIID
Expand Down Expand Up @@ -358,7 +327,7 @@ func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node)
}

} else {
er = fmt.Errorf(unsuportedStructTagMsg, annotation)
er = fmt.Errorf(unsupportedStructTagMsg, annotation)
}
}

Expand Down Expand Up @@ -395,33 +364,33 @@ func unmarshalAttribute(

// Handle field of type []string
if fieldValue.Type() == reflect.TypeOf([]string{}) {
value, err = handleStringSlice(attribute, args, fieldType, fieldValue)
value, err = handleStringSlice(attribute)
return
}

// Handle field of type time.Time
if fieldValue.Type() == reflect.TypeOf(time.Time{}) ||
fieldValue.Type() == reflect.TypeOf(new(time.Time)) {
value, err = handleTime(attribute, args, fieldType, fieldValue)
value, err = handleTime(attribute, args, fieldValue)
return
}

// Handle field of type struct
if fieldValue.Type().Kind() == reflect.Struct {
value, err = handleStruct(attribute, args, fieldType, fieldValue)
value, err = handleStruct(attribute, fieldValue)
return
}

// Handle field containing slice of structs
if fieldValue.Type().Kind() == reflect.Slice &&
reflect.TypeOf(fieldValue.Interface()).Elem().Kind() == reflect.Struct {
value, err = handleStructSlice(attribute, args, fieldType, fieldValue)
value, err = handleStructSlice(attribute, fieldValue)
return
}

// JSON value was a float (numeric)
if value.Kind() == reflect.Float64 {
value, err = handleNumeric(attribute, args, fieldType, fieldValue)
value, err = handleNumeric(attribute, fieldType, fieldValue)
return
}

Expand All @@ -440,11 +409,7 @@ func unmarshalAttribute(
return
}

func handleStringSlice(
attribute interface{},
args []string,
fieldType reflect.Type,
fieldValue reflect.Value) (reflect.Value, error) {
func handleStringSlice(attribute interface{}) (reflect.Value, error) {
v := reflect.ValueOf(attribute)
values := make([]string, v.Len())
for i := 0; i < v.Len(); i++ {
Expand All @@ -454,11 +419,7 @@ func handleStringSlice(
return reflect.ValueOf(values), nil
}

func handleTime(
attribute interface{},
args []string,
fieldType reflect.Type,
fieldValue reflect.Value) (reflect.Value, error) {
func handleTime(attribute interface{}, args []string, fieldValue reflect.Value) (reflect.Value, error) {
var isIso8601 bool
v := reflect.ValueOf(attribute)

Expand Down Expand Up @@ -507,7 +468,6 @@ func handleTime(

func handleNumeric(
attribute interface{},
args []string,
fieldType reflect.Type,
fieldValue reflect.Value) (reflect.Value, error) {
v := reflect.ValueOf(attribute)
Expand Down Expand Up @@ -584,12 +544,12 @@ func handlePointer(
concreteVal = reflect.ValueOf(&cVal)
case map[string]interface{}:
var err error
concreteVal, err = handleStruct(attribute, args, fieldType, fieldValue)
concreteVal, err = handleStruct(attribute, fieldValue)
if err != nil {
return reflect.Value{}, newErrUnsupportedPtrType(
reflect.ValueOf(attribute), fieldType, structField)
}
return concreteVal.Elem(), err
return concreteVal, err
default:
return reflect.Value{}, newErrUnsupportedPtrType(
reflect.ValueOf(attribute), fieldType, structField)
Expand All @@ -605,37 +565,42 @@ func handlePointer(

func handleStruct(
attribute interface{},
args []string,
fieldType reflect.Type,
fieldValue reflect.Value) (reflect.Value, error) {
model := reflect.New(fieldValue.Type())

data, err := json.Marshal(attribute)
if err != nil {
return model, err
return reflect.Value{}, err
}

err = json.Unmarshal(data, model.Interface())
node := new(Node)
if err := json.Unmarshal(data, &node.Attributes); err != nil {
return reflect.Value{}, err
}

if err != nil {
return model, err
var model reflect.Value
if fieldValue.Kind() == reflect.Ptr {
model = reflect.New(fieldValue.Type().Elem())
} else {
model = reflect.New(fieldValue.Type())
}

return model, err
if err := unmarshalNode(node, model, nil); err != nil {
return reflect.Value{}, err
}


return model, nil
}

func handleStructSlice(
attribute interface{},
args []string,
fieldType reflect.Type,
fieldValue reflect.Value) (reflect.Value, error) {
models := reflect.New(fieldValue.Type()).Elem()
dataMap := reflect.ValueOf(attribute).Interface().([]interface{})
for _, data := range dataMap {
model := reflect.New(fieldValue.Type().Elem()).Elem()
modelType := model.Type()

value, err := handleStruct(data, []string{}, modelType, model)
value, err := handleStruct(data, model)

if err != nil {
continue
Expand Down
31 changes: 15 additions & 16 deletions request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1013,8 +1013,8 @@ func sampleSerializedEmbeddedTestModel() *Blog {

func TestUnmarshalNestedStructPtr(t *testing.T) {
type Director struct {
Firstname string `json:"firstname"`
Surname string `json:"surname"`
Firstname string `jsonapi:"attr,firstname"`
Surname string `jsonapi:"attr,surname"`
}
type Movie struct {
ID string `jsonapi:"primary,movies"`
Expand Down Expand Up @@ -1058,7 +1058,6 @@ func TestUnmarshalNestedStructPtr(t *testing.T) {
}

func TestUnmarshalNestedStruct(t *testing.T) {

boss := map[string]interface{}{
"firstname": "Hubert",
"surname": "Farnsworth",
Expand All @@ -1074,22 +1073,22 @@ func TestUnmarshalNestedStruct(t *testing.T) {
"name": "Planet Express",
"boss": boss,
"founded-at": "2016-08-17T08:27:12Z",
"teams": []Team{
Team{
Name: "Dev",
Members: []Employee{
Employee{Firstname: "Sean"},
Employee{Firstname: "Iz"},
"teams": []map[string]interface{}{
map[string]interface{}{
"name": "Dev",
"members": []map[string]interface{}{
map[string]interface{}{"firstname": "Sean"},
map[string]interface{}{"firstname": "Iz"},
},
Leader: &Employee{Firstname: "Iz"},
"leader": map[string]interface{}{"firstname": "Iz"},
},
Team{
Name: "DxE",
Members: []Employee{
Employee{Firstname: "Akshay"},
Employee{Firstname: "Peri"},
map[string]interface{}{
"name": "DxE",
"members": []map[string]interface{}{
map[string]interface{}{"firstname": "Akshay"},
map[string]interface{}{"firstname": "Peri"},
},
Leader: &Employee{Firstname: "Peri"},
"leader": map[string]interface{}{"firstname": "Peri"},
},
},
},
Expand Down