WebAPI Custom Model binding of complex abstract object

后端 未结 2 1538
野性不改
野性不改 2020-12-15 11:43

This is a tough one. I have an issue with binding a model from JSON. I am attempting to resolve polymorphic-ally the record supplied with the type of record that it will res

2条回答
  •  隐瞒了意图╮
    2020-12-15 12:34

    The TypeNameSerializationBinder class is not necessary anymore as well as the WebApiConfig configuration.

    First, you need to create enum for record type:

    public enum ResourceRecordTypeEnum
    {
        a,
        b
    }
    

    Then, change your "Type" field in ResourceRecord to be the enum we just created:

    public abstract class ResourceRecord
    {
        public abstract ResourceRecordTypeEnum Type { get; }
    }
    

    Now you should create these 2 classes:

    Model Binder

    public class ResourceRecordModelBinder : IModelBinder
    {
        public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
        {
            if (bindingContext.ModelType != typeof(T))
                return false;
    
            try
            {
                var json = ExtractRequestJson(actionContext);
                bindingContext.Model = DeserializeObjectFromJson(json);
                return true;
            }
            catch (JsonException exception)
            {
                bindingContext.ModelState.AddModelError("JsonDeserializationException", exception);
                return false;
            }
        }
    
        private static T DeserializeObjectFromJson(string json)
        {
            // This is the main part of the conversion
            var obj = JsonConvert.DeserializeObject(json, new ResourceRecordConverter());
            return obj;
        }
    
        private string ExtractRequestJson(HttpActionContext actionContext)
        {
            var content = actionContext.Request.Content;
            string json = content.ReadAsStringAsync().Result;
            return json;
        }
    }
    

    Converter class

    public class ResourceRecordConverter : CustomCreationConverter
    {
        private ResourceRecordTypeEnum _currentObjectType;
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var jobj = JObject.ReadFrom(reader);
            // jobj is the serialized json of the reuquest
            // It pulls from each record the "type" field as it is in requested json,
            // in order to identify which object to create in "Create" method
            _currentObjectType = jobj["type"].ToObject();
            return base.ReadJson(jobj.CreateReader(), objectType, existingValue, serializer);
        }
    
        public override ResourceRecord Create(Type objectType)
        {
            switch (_currentObjectType)
            {
                case ResourceRecordTypeEnum.a:
                    return new ARecord();
                case ResourceRecordTypeEnum.b:
                    return new BRecord();
                default:
                    throw new NotImplementedException();
            }
        }
    }
    

    Controller

    [HttpPost]
    public void Post([ModelBinder(BinderType = typeof(ResourceRecordModelBinder))] RecordCollection recordCollection)
    { 
    }
    

提交回复
热议问题