0

I have a scenario where I'm passing some data to an ASP.NET Web API method, one piece of which is a list of objects which share a base type. So, the method accepts the POST data in a single DTO parameter, as Web API expects, like so:

public HttpResponseMessage SaveConfig(SaveConfigRequest req) {
    // do stuff with req
}

The SaveConfigRequest DTO has data something like this:

public class SaveConfigRequest {
    public string Code { get; set; }
    public IEnumerable<BaseParameter> Parameters { get; set; }
}

BaseParameter is an abstract class with a couple of concrete implementations:

public abstract class BaseParameter {
    protected BaseParameter(ParameterType type) {
        this.Type = type;
    }
    public ParameterType Type { get; private set; }
    public string Name { get; set; }
}

// TODO: implement other parameters
public class DateParameter : BaseParameter {
    public DateParameter() : base(ParameterType.Date) {}
    public string DefaultDateFormula { get; set; }
}

What I need is for Web API to be able to deserialize a POST request containing a collection of parameters, so that they're deserialized into their concrete types. I'm POSTing the data serialized as JSON.

One attempt that I've made is to set the TypeNameHandling of the Json.NET formatter to Auto and adding a $type property in the JSON that I'm sending, but that still results in an empty collection in the method.

A second attempt has been to create a custom model binder for the BaseParameter type, but I think it's not getting triggered because BaseParameter isn't the model, just a part of it. Perhaps I haven't configured it correctly (I've tried a ModelBinder attribute on BaseParameter and calling configuration.BindParameter(typeof(BaseParameter), new ParameterModelBinder()) in App_Start).

So, my question is what is the most straightforward way to allow part of my Web API method's model parameter to accept a collection of values of an abstract type?

bdukes
  • 152,002
  • 23
  • 148
  • 175
  • My experience is JSON is difficult to use polymorphic data structures unless you are using C# are both sides (because the .NET JSON contract serializer embeds the namespace in the JSON) and other languages do not understand that. will you be calling the webservices with client built in C#? – tatmanblue Aug 21 '15 at 21:40
  • You need to use the setting `TypeNameHandling = TypeNameHandling.Auto` in **deserialization** as well as serialization. If you leave it as `TypeNameHandling.None` the `"$type"` is ignored in deserialization. – dbc Aug 21 '15 at 22:27
  • @dbc, I'm seeing it to Auto in the web API formatter config, does that not handle deserialization? – bdukes Aug 23 '15 at 19:16
  • @dbc I just tried implementing a `JsonCoverter` as mentioned in the answer you linked to, but it's not getting called, when registered via the `JsonCoverter` attribute on `BaseParameter` or via `configuration.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new ParameterJsonConverter())`. In fact, its `CanConvert` method was called for every property except the collection. – bdukes Aug 24 '15 at 16:17
  • @tatmanblue the client is in the browser, not in C#, but I've tried manually adding the type annotation that Json.NET is looking for and it didn't seem to help. – bdukes Aug 24 '15 at 16:18
  • Can you create a [mcve](http://stackoverflow.com/help/mcve) of how you are deserializing the JSON? It sounds like that may be the cause of your problem. – dbc Aug 24 '15 at 18:54
  • @dbc I'm just deserializing directly through Web API (i.e. posting JSON to a Web API endpoint), not calling any Json.NET APIs directly. I can put together a basic Web API site if it'd help… – bdukes Aug 25 '15 at 14:16
  • Could you save yourself a lot of code and complexity by simply creating a route for each implementor? So you'd have `SaveDateConfigRequest()` and `SaveOtherConfigRequest()` etc – timothyclifford Aug 25 '15 at 14:58
  • @timothyclifford unfortunately, it's a valid case to be passed multiple parameters of different types – bdukes Aug 25 '15 at 17:03
  • 1
    @dbc in putting together an MCVE I figured out that my other attempts were conflicting with the `JsonConverter`, and just using that seemed to work (even without setting `TypeNameHandling.Auto`, having a `$type` property in the JSON skipped my `JsonConverter` for some reason). Thanks! – bdukes Aug 25 '15 at 17:04

0 Answers0