Bind string to class in controller method - c#

I would like to bind submission of JSON like this
{
"id": 1,
"name": "bob",
"phone": "(425) 882-8080"
}
to...
class Model
{
public int Id { get; set; }
public string Name { get; set; }
public PhoneNumber Phone { get; set; }
}
where the PhoneNumber class is able to bind to the phone string in the JSON.
The idea was the use a json.net custom converter like:
class Model
{
public int Id { get; set; }
public string Name { get; set; }
[JsonConverter(typeof(PhoneNumberCoverter))]
public PhoneNumber Phone { get; set; }
}
The problem is that MVC is not even trying to use the ReadJson method. Model.Phone == null.
I have tried a few things. Hoping that if I had implicit operative overrides to and from string for the PhoneNumber class, it may just do it automatically. Nope.
What is the correct way to customize model binding for this scenario?

I think you expect that when your action method like
public ActionResult Post(Model myModel)
{
// you expect that myModel should has used JSonConverter and provide you data.
}
Above thing will not work as you expected and reason for this JsonConvertor attribute is specific Json.net and not MVC attribute so DefaultModelBinder will not consider that attribute to bind your data.
Possible correct and simple way to do (But not strongly type)
public ActionResult Post(string jsonData)
{
// Now here use Json.net JsonConvert to deserialize string to object of Type Model.
}
Another solution to is to build your own custom modelbinder and use Json.net inside that.

Related

Swagger not showing broken down array object - ASP.net Core

I have the following C# ASP.Net Core Web API controller method for creating an "entity" using a POST:
[HttpPost("example")]
[SwaggerResponse(200,"Ok")]
public async Task<IActionResult> Create([FromForm]MyModel create)
{
return Ok();
}
The model is defined as this:
public class MyModel
{
public string PropA { get; set; }
public string PropB { get; set; }
public List<OtherProp> Other { get; set; }
}
public class OtherProp
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
}
When this is shown in swagger, you can see the "Other" property array looks like this:
Instead of the broken down model. How do I get swagger to breakdown this model?
Your are missing the Swagger Decorator Attributes, follow this below and replace the attributes with your specific Types / MyModel
Since you didn't put up your actual code, to see how it works, use the default samples for e.g. you can Install my Swashbuckle.Examples NuGet package. Decorate your methods with the new SwaggerResponseExample attributes below and you will see it work just fine!
// These attributes will help with your nested objects
[SwaggerResponse(HttpStatusCode.OK, Type=typeof(IEnumerable<Country>))]
[SwaggerResponseExample(HttpStatusCode.OK, typeof(CountryExamples))]
[SwaggerResponse(HttpStatusCode.BadRequest, Type = typeof(IEnumerable<ErrorResource>))]
public async Task<HttpResponseMessage> Get(string lang)
Also ensure you have it configured like so
configuration
.EnableSwagger(c =>
{
c.OperationFilter<ExamplesOperationFilter>();
})
.EnableSwaggerUi();

Why isn't my post JSON object getting serialized?

Why isn't my POST JSON object getting serialized? I'm using Web API 2.0. My controller route looks like this:
[HttpPost]
[Route]
public async Task<IHttpActionResult> AddUserAsync([FromBody] User user)
{
//do some stuff
}
My User object looks like this:
public class User
{
Guid Id { get; set; }
string Name { get; set; }
}
When I pass the following JSON object the Id and Name props get serialized with null values:
{
"Id": "895C4492-B751-462C-9738-C6CB4E94E21F",
"Name": "Joe System"
}
Do I need to decorate User with [DataContract] or something like that?
How to manage this in Web API 2?
Your properties are not public. You need to make them public
public class User {
public Guid Id { get; set; }
public string Name { get; set; }
}
The model binder inspects the intended object type and populates public properties.
Reference Parameter Binding in ASP.NET Web API

How to pass json data to abstract class?

public abstract class AbstractSearch
{
public string Property { get; set; }
public string TargetTypeName { get; set; }
}
public class DateSearch : AbstractSearch
{
public DateTime? SearchTerm { get; set; }
public DateComparators Comparator { get; set; }
}
public enum DateComparators
{
Less,
LessOrEqual,
Equal,
GreaterOrEqual,
Greater,
InRange
}
public class SearchViewModel
{
public IEnumerable<AbstractSearch> SearchCriteria { get; set; }
}
How to pass above properties to SearchViewModel class from clientside JSON.
I need to pass Property, TargetTypeName, SearchTerm, Comparator from javascript to webapi through Json.
Iam using Web api 2.0 as Serverside. Is it possible to pass Json parameter into Inherited class?
If you specify the concrete type when serializing on the client-side you can make this work using TypeNameHandling. You specify the type using a $type property in the JSON.
The model-binder won't be able to instantiate an abstract-class.
Configure JSON.NET:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto;
Then post your JSON in this format:
{
"$type": "Namespace.DateSearch, AssemblyName"
"Property": "..."
"etc": ""
}

Strongly typed AND arbitrary properties in Web API request object

I have an object that looks pretty much like this:
public class MyNiceRequest
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public string SomeOptionalField { get; set; }
}
And my controller looks like the following, pretty standard still:
public MyNiceResponse Post(MyNiceRequest request) {
...
}
From the front end of the calling application, I want to include more fields than the three specified in the object. These fields are runtime generated (controlled via an admin interface), so I cannot apply them to my request class. However, I've not found a good way to retrieve them in the controller.
I can make my request object (MyNiceRequest) inherit from Dictionary<string,string> - then I'll get them all, but they won't be bound to their respective properties on the strongly typed class (seems like the Dictionary is bound before the rest in whatever model binder is used). Plus, more importantly, validation - which is crucial to the application - stops working.
I have seen this question, but it doesn't give me anything as the Request.Content.Read...-methods give me empty results (since it's already read and bound to a model?).
Let's say I want the following fields from the front end:
FirstName (should bind to strongly typed, nowhere else)
LastName (should bind to strongly typed, nowhere else)
SomeOptionalField (should bind to strongly typed, nowhere else)
RuntimeGenerated1 (should end up in dictionary)
RuntimeGenerated2 (should end up in dictionary)
I want one of two solutions:
Either be able to inherit from Dictionary<string,string>, but let the dictionary be bound AFTER the strongly typed properties to let the validation do it's work
Have a separate property on MyNiceRequest that could be something like Dictionary<string,string> TheRest { get; set; } and bind that to the remaining incoming properties somewhere.
Rewriting the front end to pass in the runtime generated fields as a separate collection is not an option.
..and can this at all be achieved by reusing/reordering existing stuff, or will I have to write a complete media type formatter and/or model binder from scratch?
For application/json content type, you can use DynamicObject with WebAPI's default JSON formatter.
public class MyNiceRequest : DynamicObject
{
private Dictionary<string, string> _dynamicMembers = new Dictionary<string, string>();
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public string SomeOptionalField { get; set; }
[JsonIgnore]
public Dictionary<string, string> DynamicMembers
{
get { return _dynamicMembers; }
}
public override bool TryGetMember(GetMemberBinder binder, out object value)
{
string stringValue;
var isFound = _dynamicMembers.TryGetValue(binder.Name, out stringValue);
value = stringValue;
return isFound;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (value is string)
{
_dynamicMembers[binder.Name] = (string)value;
return true;
}
return false;
}
}
Edit
If you want the object to be serialized with the same format, implement IDictionary<string, string>. This is easy, just delegate the interface implementation to _dynamicMembers
This solution doesn't work with the default XML and x-www-form-urlencoded formatters :(
You can have the DTO like this.
public class MyNiceRequest
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public string SomeOptionalField { get; set; }
public Dictionary<string, string> TheRest { get; set; }
}
If you post a JSON in request body, like this {"firstname":"arve", "therest":{"key":"value"}}, FirstName property will be populated. TheRest will have an item with key "key" and value "value". Of course, ModelState.IsValid will be false, since JSON did not contain LastName.

Custom mapping with Json.NET

I'm trying to map JSON that looks like
"ids": {
"id": {
"#value":"6763754764235874140"
}
}
And I'd like to map it onto a couple of classes that look like
class Property
{
public Ids Ids { get; set; }
}
class Ids
{
public string Id { get; set; }
}
So basically I want to stuff the value of ids/id/#value from the JSON document into Ids.Id in the class architecture. From browsing the documentation, I thought I could use something like
[JsonProperty(ItemConverterType=typeof(IdConverter))]
public string Id { get; set; }
and provide a custom JsonConverter subclass named IdConverter. When I do, though, my IdConverter.ReadJson never gets called. What am I doing wrong?
Looks like the answer was that ItemConverterType is for converting items in an array. Double-annotating the property with JsonProperty and JsonConverter attributes works:
[JsonProperty(PropertyName="id")]
[JsonConverter(typeof(IdConverter))]
public string Id { get; set; }

Categories

Resources