WebApi Controller model serialization issue - c#

I'm facing little stranger issue with Web API controller. I have a collection which is being passed in an action of api controller. Object being used is collection is having 4 properties.
My action is able to accept collection parameter when it's properties are in specific order. See below :-
[HttpPost]
public ForexRates UpdateRates([FromBody] Rates rates)
{
// TODO: Obviously code :)
return rates;
}
This code is being place in API controller & calling from Postman. See below:-
<rates>
<rate>
<id>fefef</id>
<rate>35353.333</rate>
<series>dfefge</series>
<series-order>sfefefef</series-order>
</rate></rates>
If I change the order of the properties I started getting null value in my action. Can some one please explain this :)
Models
public class Rate
{
public string Id { get; set; }
public string Date { get; set; }
public double Rate { get; set; }
}
public class Rates : Collection<ForexRate>
{
}

You will need to control the order with which your XML is serialized. Use XmlElementAttribute and specify the Order.
There is a similar question here
FYI, I suppose there is no way for you to change the order of the properties, while you supply from PostMan to your WebApi service. You will need to follow the exact order.
If you don't wanna do that, then pass this Xml as a string parameter and then parse it inside a method.

The default binder can have issues when the same name is used in different places during binding.
In your case you've got Rate.Rate - both class name and property name. Try changing your class to (and corresponding xml for the post) :
public class Rate
{
public string Id { get; set; }
public string Date { get; set; }
public double Value { get; set; }
}
and then try changing the order.
While I don't have a definitive reason why it works in one order and not another, it's likely that when it gets to the Rate(double) value it tries to create a new Rate(object) but doesn't have the correct properties (as its just a double).
A more complicated solution would be to write a specific model binder for the Rate object.

The issue has to do with the DataContractSerializer which expects the elements to occur in a specific order (alphabetical with some consideration given to inheritance). That's the default serializer used when creating a Web API project.
You can override this and specify a different serializer during API Configuration like this:
GlobalConfiguration.Configuration.Formatters.XmlFormatter
.SetSerializer<SomeType>(new XmlSerializer(typeof(SomeType)));

Related

When returning a list of an interface from an API controller, how to include the subclasses' properties in the JSON response?

Situation
I'm writing an application that can display different types of charts, with a vanilla JS front-end and an ASP.NET 6 API back-end. There are different types of charts in the application, like LineChart and PieChart. These different chart types all implement the interface IChart. The IChart interface has properties for information like the ID and name of the chart, whereas the LineChart, for example, has properties for the name of the X- and Y-axis.
Here are the model classes as minimum example:
IChart.cs:
public interface IChart
{
public int Id { set; get; }
public string Title { set; get; }
public string Description { set; get; }
}
LineChart.cs:
public class LineChart : IChart
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string xAxisName { get; set; }
public string yAxisName { get; set; }
}
My Problem
When I'm returning a single chart using an ASP.NET API-controller, it works just fine; if the object I return is a LineChart, I get the property xAxisName in the JSON response even if the API method return type is IChart. But when I return multiple Charts in a List<IChart>, I only get the properties provided by IChart in my JSON, so no xAxisName. How can I make a controller return all the items in the List<IChart while keeping the properties of the derived classes in the JSON result? Is there some JSON middleware setting I need to set?
As an illustration, here are a few sample API calls.
The following code returns a LineChart with the X axis name set in the JSON reponse body:
// POST: api/<ChartController>
[HttpPost("{xAxisName}")]
public IChart MakeOneChart(string xAxisName)
{
IChart chart = new LineChart() {xAxisName = xAxisName};
return chart;
}
But in the following code, the response body is not filled with values for the X axis
// GET: api/<ChartController>
[HttpPost]
public IEnumerable<IChart> MakeManyCharts()
{
IEnumerable<IChart> charts = new List<IChart>()
{
new LineChart() {xAxisName = "Duration (in minutes)"},
new LineChart() {xAxisName = "Duration (in years)"}
};
return charts;
}
People here had a similar problem. The returned object has an interface as a property and the derived classes didn't serialize properly but I bet any property defined in that interface would show.
Now I don't know much about the inner workings of Json conversion but from what I've observed, here's what I'm guessing is happening:
The standard Json serializer provided with .Net doesn't recognize the object's runtime type so it doesn't know anything about the object beneath the surface. Only exception is the outermost object you're returning. For example, when your return type is IChart, it can recognize when you return LineChart or PieChart since it's just checking the outermost object. However, when you return IEnumerable<IChart> it can't recognize the derived type of IChart objects since they're wrapped by IEnumerable. I bet if you returned List<IChart> it could recognize properties related to List<T>
There are some possible solutions:
An answer in the linked question suggests using Json.NET package from Newtonsoft
A different answer from the question suggests writing custom serializers
Another option I would say would be to try writing a custom formatter. Check the type of object and cast it like this:
if (context.Object is IEnumerable<LineChart> lineCharts)
{
// serialize the lineCharts
}
else if (context.Object is IEnumerable<PieChart> pieCharts)
{
// serialize the pieCharts
}
// return the json string
though this type of solution probably would not work if you have base interfaces as properties like in the question and you wanted the derived objects of those to be recognized in the serialization (or rather you would have to write nested if statements and check every property of every chart object in the IEnumerable and it would go on and on if you have a very complex object), but should be enough for your situation

Managing multiple versions of object in JSON

I have a class in C#, that has a number of variables. Let's call it "QuestionItem".
I have a list of this object, which the user modifies, and then sends it via JSON serialization (with Newtonsoft JSON library) to the server.
To do so, I deserialize the objects that are already in the server, as a List<QuestionItem>, then add this new modified object to the list, and then serialize it back to the server.
In order to display this list of QuestionItems to the user, I deserialize the JSON as my object, and display it somewhere.
Now, the problem is - that I want to change this QuestionItem and add some variables to it.
But I can't send this NewQuestionItem to the server, because the items in the server are of type OldQuestionItem.
How do I merge these two types, or convert the old type to the new one, while the users with the old version will still be able to use the app?
You are using an Object Oriented Language, so you might aswell use inheritance if possible.
Assuming your old QuestionItem to be:
[JsonObject(MemberSerialization.OptOut)]
public class QuestionItem
{
[JsonConstructor]
public QuestionItem(int Id, int Variant)
{
this.Id = Id;
this.Variant = Variant;
}
public int Id { get; }
public int Variant { get; }
public string Name { get; set; }
}
you can extend it by creating a child class:
[JsonObject(MemberSerialization.OptOut)]
public class NewQuestionItem : QuestionItem
{
private DateTime _firstAccess;
[JsonConstructor]
public NewQuestionItem(int Id, int Variant, DateTime FirstAccess) : base(Id, Variant)
{
this.FirstAccess = FirstAccess;
}
public DateTime FirstAccess { get; }
}
Note that using anything different than the default constructor for a class requires you to use the [JsonConstructor] Attribute on this constructor and every argument of said constructor must be named exactly like the corresponding JSON properties. Otherwise you will get an exception, because there is no default constructor available.
Your WebAPI will now send serialized NewQuestionItems, which can be deserialized to QuestionItems. In fact: By default, JSON.NET as with most Json libraries, will deserialize it to any object if they have at least one property in common. Just make sure that any member of the object you want to serialize/desreialize can actually be serialized.
You can test the example above with the following three lines of code:
var newQuestionItem = new NewQuestionItem(1337, 42, DateTime.Now) {Name = "Hello World!"};
var jsonString = JsonConvert.SerializeObject(newQuestionItem);
var oldQuestionItem = JsonConvert.DeserializeObject<QuestionItem>(jsonString);
and simply looking at the property values of the oldQuestionItem in the debugger.
So, this is possible as long as your NewQuestionItem only adds properties to an object and does neither remove nor modify them.
If that is the case, then your objects are different and thus, requiring completely different objects with a different URI in your API, as long as you still need to maintain the old instance on the existing URI.
Which brings us to the general architecture:
The most clean and streamline approach to what you are trying to achieve is to properly version your API.
For the purpose of this link I am assuming an Asp.NET WebApi, since you are handling the JSON in C#/.NET. This allows different controller methods to be called upon different versions and thus, making structural changes the resources your API is providing depending on the time of the implementation. Other API will provide equal or at least similar features or they can be implemented manually.
Depending on the amount and size of the actual objects and potential complexity of the request- and resultsets it might also be worth looking into wrapping requests or responses with additional information. So instead of asking for an object of type T, you ask for an Object of type QueryResult<T> with it being defined along the lines of:
[JsonObject(MemberSerialization.OptOut)]
public class QueryResult<T>
{
[JsonConstructor]
public QueryResult(T Result, ResultState State,
Dictionary<string, string> AdditionalInformation)
{
this.Result = result;
this.State = state;
this.AdditionalInformation = AdditionalInformation;
}
public T Result { get; }
public ResultState State { get; }
public Dictionary<string, string> AdditionalInformation { get; }
}
public enum ResultState : byte
{
0 = Success,
1 = Obsolete,
2 = AuthenticationError,
4 = DatabaseError,
8 = ....
}
which will allow you to ship additional information, such as api version number, api version release, links to different API endpoints, error information without changing the object type, etc.
The alternative to using a wrapper with a custom header is to fully implement the HATEOAS constraint, which is also widely used. Both can, together with proper versioning, save you most of the trouble with API changes.
How about you wrapping your OldQuestionItem as a property of QuestionItem? For example:
public class NewQuestionItem
{
public OldQuestionItem OldItem { get; set; }
public string Property1 {get; set; }
public string Property2 {get; set; }
...
}
This way you can maintain the previous version of the item, yet define new information to be returned.
Koda
You can use something like
public class OldQuestionItem
{
public DateTime UploadTimeStamp {get; set;} //if less then DateTime.Now then it QuestionItem
public string Property1 {get; set; }
public string Property2 {get; set; }
...
public OldQuestionItem(NewQuestionItem newItem)
{
//logic to convert new in old
}
}
public class NewQuestionItem : OldQuestionItem
{
}
and use UploadTimeStamp as marker to understand, what Question is it.

Restful urls. View model specific

I've been going through this blog and I understand what he is saying, especially regarding the hierarchical structure (walking back along the path).
So
/objects/1/property
Removing property should give you the object with id 1, and removing the id should give you all the objects. Excellent and logical.
But I always expose my data via view models, so,
/objects/list/1 will give me the object list view model of the object with id 1. Or /objects/detail/1 will give me the object detail view model for the object with id 1.
Using this approach I have ended up with a long structure just to get a specific view model! I.e. objects/visualization/analysis/thread. Is this even restful? What I seem to be doing (subconsciously!) is structuring my restful API to match the namespace or module for where this specific view model lives (so in .NET it will be namespace: app.models.object.visualization.analysis).
How best to structure a restful endpoint like this? Is it better to have something like
objects-list/1 and objects-detail/1?
Thanks.
Example:
Sorry, I should have been more clear. I will give a .NET example. Suppose I have a cart class
public class Cart
{
public int CardId { get; set; }
public string CartName { get; set; }
public DateTime Created { get; set; }
public DateTime LastUpdated { get; set; }
public IEnumerable<CartItem> Items { get; set; }
}
With a restful design, I could expose carts as /carts, /carts/1, /carts/1/items and so on. But I always expose view models, not the actual data layer object. I.e.
public class CartListModel
{
public int CartId { get; set; }
public string CartName { get; set; }
}
and
public class CartViewModel
{
public int CartId { get; set; }
public string CartName { get; set; }
public DateTime LastUpdated { get; set; }
public IEnumerable<CartItemViewModel> Items { get; set; }
}
So this way I am only exposing the data that I actually need for a specific purpose. Now at the moment, I am exposing these view models as such /carts/list or /carts/list/1. Also /carts/view and /carts/view/1. So the original question is this restful? Do I infact need a separate endpoint for each view model? So /carts-list and /carts-view, carts-view/1 etc.
Non .NET example
Don't really know what to put here! A view model is a representation of the object, only exposing certain properties necessary to bind to a view.
So suppose my object has the following JSON structure
{
id: 1,
name: 'Cart 1',
lastUpdated: '26-Sep-2014 16:51:23',
items: [
// an array of objects
]
}
For a certain view, like a simple table, I may only need the id and the name. So I expose a restful endpoint that gives me back the following structure
{
id: 1,
name: 'Cart 1'
}
Everything else is unnecessary. For a cart edit page, I will probably need a lot more data than just the id and name. The question is, how do I structure a restful endpoint to expose these different representations of the same object?
URIs are stable
Resources are identified by URIs. The get the object with ID 1, do
GET /objects/1
To get a list of all objects, just
GET /objects
Use content negotiation
What representation of the object 1 is returned by the server is decided by content negotiation. This is done using HTTP headers, not URL path segments or query parameters. Do this:
GET /objects/1
Accept: appliction/vnd.com.example.object.detail+json
By this the client could request something you call the "detail view model".
If the client wants to get the "list view model", you could do
GET /objects/1
Accept: appliction/vnd.com.example.object.list+json
Note
The URL is the same for both requests.
The Accept headers have different values.
Don't use different URIs
Don't do any of these:
GET /objects/1/list: This would request the sub resource called list from object 1.
GET /objects/1/list: This would request another sub resource.
GET /objects/1?model=detail or GET /objects/1?model=list: These are different URIs which identify different resources.
Just try to keep your URL as simple as possible. That means that if your API was a house and you want all clothes of a person named Marie, your URL would be:
API/persons/Marie/clothes
And not:
API/house/persons/Marie/clothes/all
Sorry for the bad examples. Rest is pretty hard to explain.

Serializer ignores properties that use another serialized property

I am working on an endpoint in asp.net that serializes and returns some data, using the default serializer.
The consuming applications are transitioning between changing names for properties (in other words, some existing applications are using names like ...Vat; while newer ones are using ...Tax. I therefore need to keep both names in the response for the moment, until these changes are complete.
The return type is IList.
public class Product
{
...
public decimal PriceIncVat { get; set; }
public decimal PriceIncTax { get { return PriceIncVat; } }
public int TaxCode { get; set; }
...
}
However, when I examine the response in fiddler, only the PriceIncVat property exists in the json list of products.
I can't think of any reason why the above wouldn't work. I added TaxCode at the same time as PriceIncTax, and it is returned, so I know the code of the endpoint is up to date.
And on the client side of a newer client project we have:
public class ProductDto
{
...
public decimal PriceIncTax { get; set; }
public string TaxCode { get; set; }
...
}
Very confused here.
The serializer assumes you will need to deserialize the data some time. Hence by default only properties with a getter and a setter are considered.
When using the DataContractJsonSerializer, it's possible to turn on serialization of read-only properties using the SerializeReadOnlyTypes property (despite its rather misleading name).
Side note: Check-out the Json.NET serializer, which gives more options and better control over the (de)serialization process.

Decoration on ViewModel property to use a different name for binding

On MVC3, is there a way to decorate a ViewModel property in order to get the DefaultModelBinder to use a different name for it in the request?
For example, suppose you have the following view model:
public class SomeModel
{
public string Direction {get;set;}
}
But the parameter coming in is Dir from an external source (such as some third-party component, for example).
I know a custom model binder could handle that, but I assume there must be a way to decorate the property, similar to the way action parameters can use Bind(Prefix="...") in order to define that mapping.
You could always create another Property:
public class SomeModel
{
public string Direction {get;set;}
public string Dir
{
get { return this.Direction; }
set { this.Direction = value; }
}
}
I'd also mention that the ViewModel used in a view (cshtml/vbhtml) does not have to be the same ViewModel used on the Post Method.
OK, so after more research looking at similar questions and seeing the feedback here as well, it seems that the answer to my question is basically "NO".
There is no out-of-the-box way, so either custom binders must be used or or the properties should be renamed.
A similar question with a more detailed answer can be found here: How to bind URL parameters to model properties with different names
I was able to accomplish this in ASP.NET MVC Core using the FromForm attribute.
public class DataTableOrder
{
public int Column { get; set; }
[FromForm(Name = "Dir")]
public string Direction { get; set; }
}
Documentation: https://docs.asp.net/en/latest/mvc/models/model-binding.html#customize-model-binding-behavior-with-attributes
However, depending if you do a GET or a POST, you might want to use [FromQuery] instead of [FromForm] I suppose.

Categories

Resources