diff --git a/models_test.go b/models_test.go index eb38c98..d443378 100644 --- a/models_test.go +++ b/models_test.go @@ -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"` } diff --git a/request.go b/request.go index ff426ce..b9883f2 100644 --- a/request.go +++ b/request.go @@ -13,7 +13,7 @@ import ( ) const ( - unsuportedStructTagMsg = "Unsupported jsonapi tag annotation, %s" + unsupportedStructTagMsg = "Unsupported jsonapi tag annotation, %s" ) var ( @@ -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 @@ -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 @@ -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) } } @@ -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 } @@ -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++ { @@ -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) @@ -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) @@ -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) @@ -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()) - - if err != nil { - return model, err + node := new(Node) + if err := json.Unmarshal(data, &node.Attributes); err != nil { + return reflect.Value{}, err } - return model, err + var model reflect.Value + if fieldValue.Kind() == reflect.Ptr { + model = reflect.New(fieldValue.Type().Elem()) + } else { + model = reflect.New(fieldValue.Type()) + } + + 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 diff --git a/request_test.go b/request_test.go index 8ee4a5a..111b5fb 100644 --- a/request_test.go +++ b/request_test.go @@ -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"` @@ -1058,7 +1058,6 @@ func TestUnmarshalNestedStructPtr(t *testing.T) { } func TestUnmarshalNestedStruct(t *testing.T) { - boss := map[string]interface{}{ "firstname": "Hubert", "surname": "Farnsworth", @@ -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"}, }, }, },