diff --git a/request.go b/request.go index e9ea55b..64fc7f1 100644 --- a/request.go +++ b/request.go @@ -309,9 +309,33 @@ func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node) modelValue := model.Elem() modelType := modelValue.Type() + polyrelationFields := map[string]reflect.Type{} var er error + // preprocess the model to find polyrelation fields + for i := 0; i < modelValue.NumField(); i++ { + fieldValue := modelValue.Field(i) + fieldType := modelType.Field(i) + + args, err := getStructTags(fieldType) + if err != nil { + er = err + break + } + + if len(args) < 2 { + continue + } + + annotation := args[0] + name := args[1] + + if annotation == annotationPolyRelation { + polyrelationFields[name] = fieldValue.Type() + } + } + for i := 0; i < modelValue.NumField(); i++ { fieldValue := modelValue.Field(i) fieldType := modelType.Field(i) @@ -474,6 +498,10 @@ func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node) continue } + if pFieldType, ok := polyrelationFields[args[1]]; ok && fieldValue.Type() != pFieldType { + continue + } + // This will hold either the value of the choice type model or the actual // model, depending on annotation m := reflect.New(fieldValue.Type().Elem()) diff --git a/request_test.go b/request_test.go index 350ba6e..1408ad9 100644 --- a/request_test.go +++ b/request_test.go @@ -847,6 +847,50 @@ func Test_UnmarshalPayload_polymorphicRelations_omitted(t *testing.T) { } } +func Test_UnmarshalPayload_polymorphicRelations_deprecatedRelation(t *testing.T) { + type withDeprecatedRelation struct { + ID string `jsonapi:"primary,blogs"` + Title string `jsonapi:"attr,title"` + Media *OneOfMedia `jsonapi:"polyrelation,media"` + Image *Image `jsonapi:"relation,media"` // Deprecated + } + + in := bytes.NewReader([]byte(`{ + "data": [{ + "type": "blogs", + "id": "3", + "attributes": { + "title": "Hello, World" + }, + "relationships": { + "media": { + "data": { + "type": "videos", + "id": "123" + } + } + } + }] + }`)) + + model := reflect.TypeOf(new(withDeprecatedRelation)) + + out, err := UnmarshalManyPayload(in, model) + if err != nil { + t.Fatal(err) + } + + result := out[0].(*withDeprecatedRelation) + + if result.Title != "Hello, World" { + t.Errorf("expected Title %q but got %q", "Hello, World", result.Title) + } + + if result.Media.Video.ID != "123" { + t.Fatalf("expected Video to be \"123\", but got %+v", result.Media.Video) + } +} + func Test_choiceStructMapping(t *testing.T) { cases := []struct { val reflect.Type