forked from Mirrors/jsonapi
Show type for ErrUnsupportedPtrType
to aid in troubleshooting. Before: ``` Pointer type in struct is not supported ``` After: ``` jsonapi: Can't unmarshal true (bool) to struct field `Name`, which is a pointer to `string` ```
This commit is contained in:
parent
0400041771
commit
a3b3bb2cb5
31
request.go
31
request.go
|
@ -27,13 +27,24 @@ var (
|
||||||
// (numeric) but the Struct field was a non numeric type (i.e. not int, uint,
|
// (numeric) but the Struct field was a non numeric type (i.e. not int, uint,
|
||||||
// float, etc)
|
// float, etc)
|
||||||
ErrUnknownFieldNumberType = errors.New("The struct field was not of a known number type")
|
ErrUnknownFieldNumberType = errors.New("The struct field was not of a known number type")
|
||||||
// ErrUnsupportedPtrType is returned when the Struct field was a pointer but
|
|
||||||
// the JSON value was of a different type
|
|
||||||
ErrUnsupportedPtrType = errors.New("Pointer type in struct is not supported")
|
|
||||||
// ErrInvalidType is returned when the given type is incompatible with the expected type.
|
// ErrInvalidType is returned when the given type is incompatible with the expected type.
|
||||||
ErrInvalidType = errors.New("Invalid type provided") // I wish we used punctuation.
|
ErrInvalidType = errors.New("Invalid type provided") // I wish we used punctuation.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrUnsupportedPtrType is returned when the Struct field was a pointer but
|
||||||
|
// the JSON value was of a different type
|
||||||
|
func ErrUnsupportedPtrType(rf reflect.Value, t reflect.Type, structField reflect.StructField) error {
|
||||||
|
typeName := t.Elem().Name()
|
||||||
|
kind := t.Elem().Kind()
|
||||||
|
if kind.String() != "" && kind.String() != typeName {
|
||||||
|
typeName = fmt.Sprintf("%s (%s)", typeName, kind.String())
|
||||||
|
}
|
||||||
|
return fmt.Errorf(
|
||||||
|
"jsonapi: Can't unmarshal %+v (%s) to struct field `%s`, which is a pointer to `%s`",
|
||||||
|
rf, rf.Type().Kind(), structField.Name, typeName,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// UnmarshalPayload converts an io into a struct instance using jsonapi tags on
|
// UnmarshalPayload converts an io into a struct instance using jsonapi tags on
|
||||||
// struct fields. This method supports single request payloads only, at the
|
// struct fields. This method supports single request payloads only, at the
|
||||||
// moment. Bulk creates and updates are not supported yet.
|
// moment. Bulk creates and updates are not supported yet.
|
||||||
|
@ -256,7 +267,8 @@ func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
value, err := unmarshalAttribute(attribute, args, fieldType.Type, fieldValue)
|
structField := fieldType
|
||||||
|
value, err := unmarshalAttribute(attribute, args, structField, fieldValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
er = err
|
er = err
|
||||||
break
|
break
|
||||||
|
@ -363,9 +375,10 @@ func assign(field, value reflect.Value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalAttribute(attribute interface{}, args []string, fieldType reflect.Type, fieldValue reflect.Value) (value reflect.Value, err error) {
|
func unmarshalAttribute(attribute interface{}, args []string, structField reflect.StructField, fieldValue reflect.Value) (value reflect.Value, err error) {
|
||||||
|
|
||||||
value = reflect.ValueOf(attribute)
|
value = reflect.ValueOf(attribute)
|
||||||
|
fieldType := structField.Type
|
||||||
|
|
||||||
// Handle field of type []string
|
// Handle field of type []string
|
||||||
if fieldValue.Type() == reflect.TypeOf([]string{}) {
|
if fieldValue.Type() == reflect.TypeOf([]string{}) {
|
||||||
|
@ -399,7 +412,7 @@ func unmarshalAttribute(attribute interface{}, args []string, fieldType reflect.
|
||||||
|
|
||||||
// Field was a Pointer type
|
// Field was a Pointer type
|
||||||
if fieldValue.Kind() == reflect.Ptr {
|
if fieldValue.Kind() == reflect.Ptr {
|
||||||
value, err = handlePointer(attribute, args, fieldType, fieldValue)
|
value, err = handlePointer(attribute, args, fieldType, fieldValue, structField)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -527,7 +540,7 @@ func handleNumeric(attribute interface{}, args []string, fieldType reflect.Type,
|
||||||
return numericValue, nil
|
return numericValue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlePointer(attribute interface{}, args []string, fieldType reflect.Type, fieldValue reflect.Value) (reflect.Value, error) {
|
func handlePointer(attribute interface{}, args []string, fieldType reflect.Type, fieldValue reflect.Value, structField reflect.StructField) (reflect.Value, error) {
|
||||||
t := fieldValue.Type()
|
t := fieldValue.Type()
|
||||||
var concreteVal reflect.Value
|
var concreteVal reflect.Value
|
||||||
|
|
||||||
|
@ -543,11 +556,11 @@ func handlePointer(attribute interface{}, args []string, fieldType reflect.Type,
|
||||||
case uintptr:
|
case uintptr:
|
||||||
concreteVal = reflect.ValueOf(&cVal)
|
concreteVal = reflect.ValueOf(&cVal)
|
||||||
default:
|
default:
|
||||||
return reflect.Value{}, ErrUnsupportedPtrType
|
return reflect.Value{}, ErrUnsupportedPtrType(reflect.ValueOf(attribute), fieldType, structField)
|
||||||
}
|
}
|
||||||
|
|
||||||
if t != concreteVal.Type() {
|
if t != concreteVal.Type() {
|
||||||
return reflect.Value{}, ErrUnsupportedPtrType
|
return reflect.Value{}, ErrUnsupportedPtrType(reflect.ValueOf(attribute), fieldType, structField)
|
||||||
}
|
}
|
||||||
|
|
||||||
return concreteVal, nil
|
return concreteVal, nil
|
||||||
|
|
|
@ -121,12 +121,65 @@ func TestUnmarshalPayloadWithPointerAttr_AbsentVal(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalToStructWithPointerAttr_BadType(t *testing.T) {
|
func TestUnmarshalToStructWithPointerAttr_BadType_bool(t *testing.T) {
|
||||||
out := new(WithPointer)
|
out := new(WithPointer)
|
||||||
in := map[string]interface{}{
|
in := map[string]interface{}{
|
||||||
"name": true, // This is the wrong type.
|
"name": true, // This is the wrong type.
|
||||||
}
|
}
|
||||||
expectedErrorMessage := ErrUnsupportedPtrType.Error()
|
expectedErrorMessage := "jsonapi: Can't unmarshal true (bool) to struct field `Name`, which is a pointer to `string`"
|
||||||
|
|
||||||
|
err := UnmarshalPayload(sampleWithPointerPayload(in), out)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected error due to invalid type.")
|
||||||
|
}
|
||||||
|
if err.Error() != expectedErrorMessage {
|
||||||
|
t.Fatalf("Unexpected error message: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalToStructWithPointerAttr_BadType_MapPtr(t *testing.T) {
|
||||||
|
out := new(WithPointer)
|
||||||
|
in := map[string]interface{}{
|
||||||
|
"name": &map[string]interface{}{"a": 5}, // This is the wrong type.
|
||||||
|
}
|
||||||
|
expectedErrorMessage := "jsonapi: Can't unmarshal map[a:5] (map) to struct field `Name`, which is a pointer to `string`"
|
||||||
|
|
||||||
|
err := UnmarshalPayload(sampleWithPointerPayload(in), out)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected error due to invalid type.")
|
||||||
|
}
|
||||||
|
if err.Error() != expectedErrorMessage {
|
||||||
|
t.Fatalf("Unexpected error message: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalToStructWithPointerAttr_BadType_Struct(t *testing.T) {
|
||||||
|
out := new(WithPointer)
|
||||||
|
type FooStruct struct{ A int }
|
||||||
|
in := map[string]interface{}{
|
||||||
|
"name": FooStruct{A: 5}, // This is the wrong type.
|
||||||
|
}
|
||||||
|
expectedErrorMessage := "jsonapi: Can't unmarshal map[A:5] (map) to struct field `Name`, which is a pointer to `string`"
|
||||||
|
|
||||||
|
err := UnmarshalPayload(sampleWithPointerPayload(in), out)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected error due to invalid type.")
|
||||||
|
}
|
||||||
|
if err.Error() != expectedErrorMessage {
|
||||||
|
t.Fatalf("Unexpected error message: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalToStructWithPointerAttr_BadType_IntSlice(t *testing.T) {
|
||||||
|
out := new(WithPointer)
|
||||||
|
type FooStruct struct{ A, B int }
|
||||||
|
in := map[string]interface{}{
|
||||||
|
"name": []int{4, 5}, // This is the wrong type.
|
||||||
|
}
|
||||||
|
expectedErrorMessage := "jsonapi: Can't unmarshal [4 5] (slice) to struct field `Name`, which is a pointer to `string`"
|
||||||
|
|
||||||
err := UnmarshalPayload(sampleWithPointerPayload(in), out)
|
err := UnmarshalPayload(sampleWithPointerPayload(in), out)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue