WebAPI not serializing inherited class on POST? - c#

Here is my controller method:
[System.Web.Http.HttpPost]
[System.Web.Http.Route("api/exercise")]
public HttpResponseMessage CreateExercise(ExerciseDto exercise) {
Here are my classes:
public class Exercise {
[Key]
[Required]
public int ExerciseId { get; set; }
[StringLength(300, ErrorMessage = "The value cannot exceed 300 characters. ")]
public string Title { get; set; }
}
[NotMapped]
[Serializable]
public class ExerciseDto : Exercise {
public ExerciseDto(Exercise exercise) {
ExerciseId = exercise.ExerciseId;
Title = exercise.Title;
UserHasExercise = true;
}
public bool UserHasExercise { get; set; }
public List<int> SomeIds { get; set; }
}
If I use type Exercise in the API controller, the object comes through. I created the DTO to extend the POCO with some more properties, but if I use this ExerciseDto class, I get null whenever I send the same data I was sending before. What is happening?
WebAPI Config:
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.None;
config.Formatters.Remove(config.Formatters.XmlFormatter);
json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
var enumConverter = new Newtonsoft.Json.Converters.StringEnumConverter();
json.SerializerSettings.Converters.Add(enumConverter);
Update:
My solution for the time being is to scrap the idea of DTOs altogether and just extend the POCO with [NotMapped] properties:
public class Exercise {
[Key]
[Required]
public int ExerciseId { get; set; }
[StringLength(300, ErrorMessage = "The value cannot exceed 300 characters. ")]
public string Title { get; set; }
[NotMapped]
public bool UserHasExercise { get; set; }
[NotMapped]
public List<int> SomeIds { get; set; }
}
This keeps everything super simple, but I have a feeling it's not a best practice for more complicated models. Am very interested in seeing the proper way to handle this.

ExerciseDto needs a parameterless constructor in order for the Post body deserialize properly.
Update: your update is the way I would do it.

I believe the problem is that you've declared ExerciseDto as [Serializable] but you're not passing all ExerciseDto model parameters in your request (you mentioned that you pass the same data as you did for Exercise). You can either remove the [Serializable] attribute or, alternatively, add [JsonIgnore] to your model's UserHasExercise and SomeIds parameters. If you're passing the data as xml and not json, then use [IgnoreDataMember] instead of [JsonIgnore].

Related

Send a limited/reduced class to frontend

What I want
I want to send a limited/reduced class/object to frontend (as JSON). I use .NET Core 5.
What I have
I have a model class like this:
namespace Tasks.Models
{
public class Resources
{
public Guid Id { get; set; }
public string Type { get; set; }
public string Url { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime? Createdon { get; set; }
public Guid Userid { get; set; }
public Guid Taskid { get; set; }
public int Clicked { get; set; }
public byte Active { get; set; }
+++ many more properties
}
}
Now depending on the which controller that calls this model I want to have different "kind" of models. So if the resource is file I maybe want the properties Id,Type,Name. But if the resource is URL I want Id, Url, Name.
I tried setting up a method that "initialized the fields I wanted, but that also returned all properties
public static Responses FileResponse()
{
var response = new Responses()
{
Id = new Guid(),
Name = "",
Type = "File",
};
return response;
}
Now, when I call the Resources class or this method I get all properties, and returning it to the view presents all properties, but mostly as null, because I only set the three fields in the method.
What is the recommended way of solving this?
If you want to remove the field if it's null instead of showing in json with null value.
public class Resources
{
public Guid Id { get; set; }
public string Type { get; set; }
// if null, dont show it in JSON output
[JsonIgnoreAttribute(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Url { get; set; }
// if null, dont show it in JSON output
[JsonIgnoreAttribute(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Name { get; set; }
public string Description { get; set; }
public DateTime? Createdon { get; set; }
public Guid Userid { get; set; }
public Guid Taskid { get; set; }
public int Clicked { get; set; }
public byte Active { get; set; }
}
PS: Fiddle https://dotnetfiddle.net/hiMAci
It is just limiting the Resource class I am not able to do
Yep, side effect of C# being strongly typed, with object X definitely having properties Y and Z. You need differently shaped objects - either full on classes or records - that name the reduced set of properties because the serializer is going to look a tthe object and ser every property it can find.
You could make a new class for every variation - quick and easy with records, and easy to pass around inside your C#:
public record FileThing(string Id, string Type, string Name);
//make a new one and return it
new FileThing(someResources.Id, someResources.Type, someResources.Name);
Or can consider using an anonymous type if you're literally looking to put a few properties into some json, down a socket to a consuming front end (I can't quite decide what you mean by "view" - it doesn't seem to be an MVC View) that only cares about a few props out of many
So if the resource is file I maybe want the properties Id,Type,Name. But if the resource is URL I want Id, Url, Name.
public ActionResult SomeControllerMethod(){
if(isFile)
return Ok(new { someResources.Id, someResources.Type, someResources.Name });
else if(isUrl)
return Ok(new { someResources.Id, someResources.Url, someResources.Name });
}
Anonymous types are a bit harder to work with because the compiler writes the class for you, so it's tricky to do things like declare return types from methods if the method is returning an AT.. But if you're using it as some fill-in all within one method, such as a "make this and serialize it", they work well..
I think your approach is not the right one here. I tend to follow more general OO guidelines in this situation (note, some consider these a bit dated, and other solutions exist. But they are still commonly used)
You write against an interface. So let's see what you want... A guid, type and name. All other deatils aren't important.
public interface IResourceDetails
{
public Guid Id { get; }
public string Name { get; }
public string Type { get; }
}
And you can have multiple of these interfaces.
You could then implement the interfaces per type. But I would probably combine them in a base class
public abstract class ResourceBase : IResourceDetails
{
public Guid Id { get; } = new ();
public string Name { get; init; }
public string Type { get; }
public ResourceBase(string type)
{
Type = type;
}
}
Each resource type would have it's own implementation
public class FileResource : ResourceBase
{
public FileResource() : base("File") { }
// File-specific properties.
public string Description { get; init; }
public DateTime? Createdon { get; init; }
}
The response method then could be made generic and look like this
public static IActionResult Response(IResourceDetails resource)
{
return Ok(new
{
resource.Id,
resource.Name,
resource.Type,
});
}

Consume Data From this endpoint

I am getting tdata from a certain endpoint and the problem id on serialization to my classes. I want to cast the bellow data to my class but cant get how the class should be structured. Check out the data .....
{
"-LYG_AI_oGYjNBrzMlKF": {
"chatDispayText": "",
"chatId": "-LYG_AI_oGYjNBrzMlKF",
"chatName": "",
"chattype": "single",
"imageUrl": "https://wallpaper.wiki/wp-content/uploads/2017/04/wallpaper.wiki-Amazing-celebrities-hd-wallpaper-PIC-WPD004734.jpg",
"lastMessageSent": "aiye",
"lastMessageSentTime": 1549704416263,
"synched": false,
"users": {
"-LYG_AIZ5MvTbjR7DACe": "Uicpm3L15TX0c15pKCI6KUEARyB3",
"-LYG_AI_oGYjNBrzMlKE": "Xsr0z9lsqNOEytX61lJvaGz1A8F2"
}
}
}
If the data you get out the endpoint has a dynamic structure, you can make use of a key-vale pair collection or a dictionary. For instance:
JObject jObject = JObject.Parse(Data); // This would already give you a key-value pair collection
Dictionary<String,Object> collection = new Dictionary<String, Object>();
foreach(var obj in jObject){
collection.Add(obj.Key, obj.Value);
}
However, this isn't a strongly typed approach which means that it is not effective in the majority of scenarios. A better solution when dealing with endpoints would be to define a class with fixed schema, actually something you need in your code, and then map the class to the object yielded by the endpoint using a metadata struct. For example:
public class ChatInfoModel
{
[JsonProperty(Metadata.ChatId)]
public long ChatId { get; set; }
[JsonProperty(Metadata.ChatId, Required = Required.AllowNull)]
public String Message { get; set; }
}
public struct Metadata
{
public const String ChatId = "userChatId";
public const String Message = "messageTxt";
}
And then
var deserializedObject = JsonConvert.DeserializeObject<ChatInfoModel>(data);
However, if your class has the exact same naming convention (but should not necessarily follow the camelCase naming convention) for its properties as in the serialized data, the JsonProperty attribute would not be needed.
You can also deserialize the object without using JsonProperty attribute manually using the first approach, and it is actually advantageous in certain scenarios where your schema comes from a configuration file rather than a struct.
Take inspiration from the Structure below:
public class Rootobject
{
public LYG_AI_Ogyjnbrzmlkf LYG_AI_oGYjNBrzMlKF { get; set; }
}
public class LYG_AI_Ogyjnbrzmlkf
{
public string chatDispayText { get; set; }
public string chatId { get; set; }
public string chatName { get; set; }
public string chattype { get; set; }
public string imageUrl { get; set; }
public string lastMessageSent { get; set; }
public long lastMessageSentTime { get; set; }
public bool synched { get; set; }
public Users users { get; set; }
}
public class Users
{
public string LYG_AIZ5MvTbjR7DACe { get; set; }
public string LYG_AI_oGYjNBrzMlKE { get; set; }
}

Deserialize Json when name is dynamic

I use this simple API, https://exchangeratesapi.io/ and I test with this uri: https://api.exchangeratesapi.io/history?start_at=2018-01-01&end_at=2018-03-01&symbols=SEK.
I want to deserialize the 'rates' part. Here is one response sample
And here is the code
public class ExchangeRate
{
[JsonProperty(PropertyName = "end_at", Order = 1)]
public DateTime EndAt { get; set; }
[JsonProperty(PropertyName = "start_at", Order = 2)]
public DateTime StartAt { get; set; }
[JsonProperty(PropertyName = "rates", Order = 3)]
public Dictionary<string, Rate> Rates { get; set; }
[JsonProperty(PropertyName = "base", Order = 4)]
public string Base { get; set; }
}
public class Rate
{
[JsonProperty]
public Dictionary<string, double> Fields{ get; set; }
}
or
public class Rate
{
[JsonProperty]
public string CurrencyName { get; set; }
[JsonProperty]
public double CurrencyRate { get; set; }
}
And I deserilize it like this
var result = Newtonsoft.Json.JsonConvert.DeserializeObject<ExchangeRateHistory>(response.Content);
My problem is that that Fields is null. Does anyone have any suggestion?
If your key/value pair are not fixed and data must be configurable then Newtonsoft.json has one feature that to be use here and that is [JsonExtensionData]. Read more
Extension data is now written when an object is serialized. Reading and writing extension data makes it possible to automatically round-trip all JSON without adding every property to the .NET type you’re deserializing to. Only declare the properties you’re interested in and let extension data do the rest.
In your case rates key have value as dynamic data so your Rate class will be
public class Rate
{
[JsonExtensionData]
public Dictionary<string, JToken> Fields { get; set; }
}
And then you can deserialize your response content as
var result = Newtonsoft.Json.JsonConvert.DeserializeObject<ExchangeRate>(response.Content);

Custom Deserialization: use JsonConverter or custom override of Deserialize method?

My C# model Person has properties that don't map nicely to the JSON I am getting from a RESTful request.
C# Model:
class Person {
public string First { get; set; }
public string Last { get; set; }
}
JSON Response:
{
"customer_first_name": "foo",
"customer_last_name": "bar"
}
So when I deserialize the JSOn to a Person model/object I need to map customer_first_name to First and so on (am I correct?). Should I be using a JsonConverter to achieve this? Or custom override of Deserialize method? Or something else?
Just use the [JsonProperty] attribute.
class Person {
[JsonProperty(PropertyName = "customer_first_name")]
public string First { get; set; }
[JsonProperty(PropertyName = "customer_last_name")]
public string Last { get; set; }
}

Web API - Return some fields from model

I'm having this model :
public class Quiz
{
public int Id { get; set; }
public string Title { get; set; }
public int CurrentQuestion { get; set; }
[JsonIgnore]
public virtual ICollection<Question> Questions { get; set; }
}
There is [JsonIgnore] which tells JSON Serializer to ignore this field(Questions). So,I'm having an action which returns serialized Quiz without Questions. I have to implement another action which will return all fields (Questions inclusive). How can I do this ? I need both actions.
It's very good practice not to return your domain models from and API. Better way is to create view model classes and return them instead.
So in your example you'd simply create:
public class QuizViewModel
{
public int Id { get; set; }
public string Title { get; set; }
public int CurrentQuestion { get; set; }
}
and use it to return data from your API.
Obviously in some bigger classes it would be nightmare to crate the code copying all the properties, but don't worry - Automapper (http://automapper.org/) comes to rescue! :)
//Best put this line in app init code
Mapper.CrateMap<Quiz, QuizViewModel>();
//And in your API
var quiz = GetSomeQuiz();
return Mapper.Map<QuizViewModel>(quiz);
You then create another view model class with Questions field in the same way.
You need to slightly change your code as below although its very simple :)
[Serializable]
public class Quiz
{
public int Id { get; set; }
public string Title { get; set; }
public int CurrentQuestion { get; set; }
}
[Serializable]
public class QuizWithQuestions : Quiz
{
public ICollection<Question> Questions { get; set; }
}
Now when you want to include Collection of Questions as well use QuizWithQuestions class.
I had the similar problem. And my solution is:
By default I have DBContext with disable LazyLoading:
public EFDbContext()
:base("EFDbContext")
{
this.Configuration.ProxyCreationEnabled = true;
this.Configuration.LazyLoadingEnabled = false;
}
So, all navigation property (like Questions) will be NULL. Then in my WebAPIConfig I configure formatter to hide null values:
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling
= Newtonsoft.Json.ReferenceLoopHandling.Ignore;
config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling
= Newtonsoft.Json.NullValueHandling.Ignore;
When I need a result with all fields from model then I just turn on the LazyLoading in controller:
repository.SwitchLazyLoading(true);
The method in repository:
public void SwitchLazyLoading(bool value)
{
this.context.Configuration.LazyLoadingEnabled = value;
}
I do not use [JsonIgnore] I use only [IgnoreDataMember]
Please look at the Switches for LazyLoading with Repository pattern

Categories