forked from Mirrors/jsonapi
Merge pull request #6 from shwoodard/shwoodard-slemgrim-pull-99
Use jsonapi struct tags for nested attrs
This commit is contained in:
commit
417d4eb8fb
|
@ -165,14 +165,14 @@ type Company struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Team struct {
|
type Team struct {
|
||||||
Name string `json:"name"`
|
Name string `jsonapi:"attr,name"`
|
||||||
Leader *Employee `json:"leader"`
|
Leader *Employee `jsonapi:"attr,leader"`
|
||||||
Members []Employee `json:"members"`
|
Members []Employee `jsonapi:"attr,members"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Employee struct {
|
type Employee struct {
|
||||||
Firstname string `json:"firstname"`
|
Firstname string `jsonapi:"attr,firstname"`
|
||||||
Surname string `json:"surname"`
|
Surname string `jsonapi:"attr,surname"`
|
||||||
Age int `json:"age"`
|
Age int `jsonapi:"attr,age"`
|
||||||
HiredAt *time.Time `json:"hired-at,iso8601"`
|
HiredAt *time.Time `jsonapi:"attr,hired-at,iso8601"`
|
||||||
}
|
}
|
||||||
|
|
99
request.go
99
request.go
|
@ -13,7 +13,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
unsuportedStructTagMsg = "Unsupported jsonapi tag annotation, %s"
|
unsupportedStructTagMsg = "Unsupported jsonapi tag annotation, %s"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -147,7 +147,7 @@ func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
modelValue := model.Elem()
|
modelValue := model.Elem()
|
||||||
modelType := model.Type().Elem()
|
modelType := modelValue.Type()
|
||||||
|
|
||||||
var er error
|
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
|
// Convert the numeric float to one of the supported ID numeric types
|
||||||
// (int[8,16,32,64] or uint[8,16,32,64])
|
// (int[8,16,32,64] or uint[8,16,32,64])
|
||||||
var idValue reflect.Value
|
idValue, err := handleNumeric(floatValue, fieldType.Type, fieldValue)
|
||||||
switch kind {
|
if err != nil {
|
||||||
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:
|
|
||||||
// We had a JSON float (numeric), but our field was not one of the
|
// We had a JSON float (numeric), but our field was not one of the
|
||||||
// allowed numeric types
|
// allowed numeric types
|
||||||
er = ErrBadJSONAPIID
|
er = ErrBadJSONAPIID
|
||||||
|
@ -358,7 +327,7 @@ func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
er = fmt.Errorf(unsuportedStructTagMsg, annotation)
|
er = fmt.Errorf(unsupportedStructTagMsg, annotation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -395,33 +364,33 @@ func unmarshalAttribute(
|
||||||
|
|
||||||
// Handle field of type []string
|
// Handle field of type []string
|
||||||
if fieldValue.Type() == reflect.TypeOf([]string{}) {
|
if fieldValue.Type() == reflect.TypeOf([]string{}) {
|
||||||
value, err = handleStringSlice(attribute, args, fieldType, fieldValue)
|
value, err = handleStringSlice(attribute)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle field of type time.Time
|
// Handle field of type time.Time
|
||||||
if fieldValue.Type() == reflect.TypeOf(time.Time{}) ||
|
if fieldValue.Type() == reflect.TypeOf(time.Time{}) ||
|
||||||
fieldValue.Type() == reflect.TypeOf(new(time.Time)) {
|
fieldValue.Type() == reflect.TypeOf(new(time.Time)) {
|
||||||
value, err = handleTime(attribute, args, fieldType, fieldValue)
|
value, err = handleTime(attribute, args, fieldValue)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle field of type struct
|
// Handle field of type struct
|
||||||
if fieldValue.Type().Kind() == reflect.Struct {
|
if fieldValue.Type().Kind() == reflect.Struct {
|
||||||
value, err = handleStruct(attribute, args, fieldType, fieldValue)
|
value, err = handleStruct(attribute, fieldValue)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle field containing slice of structs
|
// Handle field containing slice of structs
|
||||||
if fieldValue.Type().Kind() == reflect.Slice &&
|
if fieldValue.Type().Kind() == reflect.Slice &&
|
||||||
reflect.TypeOf(fieldValue.Interface()).Elem().Kind() == reflect.Struct {
|
reflect.TypeOf(fieldValue.Interface()).Elem().Kind() == reflect.Struct {
|
||||||
value, err = handleStructSlice(attribute, args, fieldType, fieldValue)
|
value, err = handleStructSlice(attribute, fieldValue)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON value was a float (numeric)
|
// JSON value was a float (numeric)
|
||||||
if value.Kind() == reflect.Float64 {
|
if value.Kind() == reflect.Float64 {
|
||||||
value, err = handleNumeric(attribute, args, fieldType, fieldValue)
|
value, err = handleNumeric(attribute, fieldType, fieldValue)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -440,11 +409,7 @@ func unmarshalAttribute(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleStringSlice(
|
func handleStringSlice(attribute interface{}) (reflect.Value, error) {
|
||||||
attribute interface{},
|
|
||||||
args []string,
|
|
||||||
fieldType reflect.Type,
|
|
||||||
fieldValue reflect.Value) (reflect.Value, error) {
|
|
||||||
v := reflect.ValueOf(attribute)
|
v := reflect.ValueOf(attribute)
|
||||||
values := make([]string, v.Len())
|
values := make([]string, v.Len())
|
||||||
for i := 0; i < v.Len(); i++ {
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
@ -454,11 +419,7 @@ func handleStringSlice(
|
||||||
return reflect.ValueOf(values), nil
|
return reflect.ValueOf(values), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleTime(
|
func handleTime(attribute interface{}, args []string, fieldValue reflect.Value) (reflect.Value, error) {
|
||||||
attribute interface{},
|
|
||||||
args []string,
|
|
||||||
fieldType reflect.Type,
|
|
||||||
fieldValue reflect.Value) (reflect.Value, error) {
|
|
||||||
var isIso8601 bool
|
var isIso8601 bool
|
||||||
v := reflect.ValueOf(attribute)
|
v := reflect.ValueOf(attribute)
|
||||||
|
|
||||||
|
@ -507,7 +468,6 @@ func handleTime(
|
||||||
|
|
||||||
func handleNumeric(
|
func handleNumeric(
|
||||||
attribute interface{},
|
attribute interface{},
|
||||||
args []string,
|
|
||||||
fieldType reflect.Type,
|
fieldType reflect.Type,
|
||||||
fieldValue reflect.Value) (reflect.Value, error) {
|
fieldValue reflect.Value) (reflect.Value, error) {
|
||||||
v := reflect.ValueOf(attribute)
|
v := reflect.ValueOf(attribute)
|
||||||
|
@ -584,12 +544,12 @@ func handlePointer(
|
||||||
concreteVal = reflect.ValueOf(&cVal)
|
concreteVal = reflect.ValueOf(&cVal)
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
var err error
|
var err error
|
||||||
concreteVal, err = handleStruct(attribute, args, fieldType, fieldValue)
|
concreteVal, err = handleStruct(attribute, fieldValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return reflect.Value{}, newErrUnsupportedPtrType(
|
return reflect.Value{}, newErrUnsupportedPtrType(
|
||||||
reflect.ValueOf(attribute), fieldType, structField)
|
reflect.ValueOf(attribute), fieldType, structField)
|
||||||
}
|
}
|
||||||
return concreteVal.Elem(), err
|
return concreteVal, err
|
||||||
default:
|
default:
|
||||||
return reflect.Value{}, newErrUnsupportedPtrType(
|
return reflect.Value{}, newErrUnsupportedPtrType(
|
||||||
reflect.ValueOf(attribute), fieldType, structField)
|
reflect.ValueOf(attribute), fieldType, structField)
|
||||||
|
@ -605,37 +565,42 @@ func handlePointer(
|
||||||
|
|
||||||
func handleStruct(
|
func handleStruct(
|
||||||
attribute interface{},
|
attribute interface{},
|
||||||
args []string,
|
|
||||||
fieldType reflect.Type,
|
|
||||||
fieldValue reflect.Value) (reflect.Value, error) {
|
fieldValue reflect.Value) (reflect.Value, error) {
|
||||||
model := reflect.New(fieldValue.Type())
|
|
||||||
|
|
||||||
data, err := json.Marshal(attribute)
|
data, err := json.Marshal(attribute)
|
||||||
if err != nil {
|
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 {
|
||||||
if err != nil {
|
return reflect.Value{}, err
|
||||||
return model, 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(
|
func handleStructSlice(
|
||||||
attribute interface{},
|
attribute interface{},
|
||||||
args []string,
|
|
||||||
fieldType reflect.Type,
|
|
||||||
fieldValue reflect.Value) (reflect.Value, error) {
|
fieldValue reflect.Value) (reflect.Value, error) {
|
||||||
models := reflect.New(fieldValue.Type()).Elem()
|
models := reflect.New(fieldValue.Type()).Elem()
|
||||||
dataMap := reflect.ValueOf(attribute).Interface().([]interface{})
|
dataMap := reflect.ValueOf(attribute).Interface().([]interface{})
|
||||||
for _, data := range dataMap {
|
for _, data := range dataMap {
|
||||||
model := reflect.New(fieldValue.Type().Elem()).Elem()
|
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 {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -1013,8 +1013,8 @@ func sampleSerializedEmbeddedTestModel() *Blog {
|
||||||
|
|
||||||
func TestUnmarshalNestedStructPtr(t *testing.T) {
|
func TestUnmarshalNestedStructPtr(t *testing.T) {
|
||||||
type Director struct {
|
type Director struct {
|
||||||
Firstname string `json:"firstname"`
|
Firstname string `jsonapi:"attr,firstname"`
|
||||||
Surname string `json:"surname"`
|
Surname string `jsonapi:"attr,surname"`
|
||||||
}
|
}
|
||||||
type Movie struct {
|
type Movie struct {
|
||||||
ID string `jsonapi:"primary,movies"`
|
ID string `jsonapi:"primary,movies"`
|
||||||
|
@ -1058,7 +1058,6 @@ func TestUnmarshalNestedStructPtr(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalNestedStruct(t *testing.T) {
|
func TestUnmarshalNestedStruct(t *testing.T) {
|
||||||
|
|
||||||
boss := map[string]interface{}{
|
boss := map[string]interface{}{
|
||||||
"firstname": "Hubert",
|
"firstname": "Hubert",
|
||||||
"surname": "Farnsworth",
|
"surname": "Farnsworth",
|
||||||
|
@ -1074,22 +1073,22 @@ func TestUnmarshalNestedStruct(t *testing.T) {
|
||||||
"name": "Planet Express",
|
"name": "Planet Express",
|
||||||
"boss": boss,
|
"boss": boss,
|
||||||
"founded-at": "2016-08-17T08:27:12Z",
|
"founded-at": "2016-08-17T08:27:12Z",
|
||||||
"teams": []Team{
|
"teams": []map[string]interface{}{
|
||||||
Team{
|
map[string]interface{}{
|
||||||
Name: "Dev",
|
"name": "Dev",
|
||||||
Members: []Employee{
|
"members": []map[string]interface{}{
|
||||||
Employee{Firstname: "Sean"},
|
map[string]interface{}{"firstname": "Sean"},
|
||||||
Employee{Firstname: "Iz"},
|
map[string]interface{}{"firstname": "Iz"},
|
||||||
},
|
},
|
||||||
Leader: &Employee{Firstname: "Iz"},
|
"leader": map[string]interface{}{"firstname": "Iz"},
|
||||||
},
|
},
|
||||||
Team{
|
map[string]interface{}{
|
||||||
Name: "DxE",
|
"name": "DxE",
|
||||||
Members: []Employee{
|
"members": []map[string]interface{}{
|
||||||
Employee{Firstname: "Akshay"},
|
map[string]interface{}{"firstname": "Akshay"},
|
||||||
Employee{Firstname: "Peri"},
|
map[string]interface{}{"firstname": "Peri"},
|
||||||
},
|
},
|
||||||
Leader: &Employee{Firstname: "Peri"},
|
"leader": map[string]interface{}{"firstname": "Peri"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue