JSON.NET - Ignore properties for serialization - c#

I am currently developing a ASP.NET WebAPI using JSON.NET.
I am looking to reduce traffic and want to ignore certain properties of my models for serialization, i.e. I don't want to return them in my JSON response, but I want to accept them when they are passed to my endpoint.
Example class
public class User {
public int Id { get; set; }
public string Name { get; set; }
public string Role { get; set; }
}
Use-cases
I've got a POST endpoint that takes a User model as a parameter. The request contains Name and Role. Those props should be parsed into my User.
I've got a GET endpoint that returns a User. I only want the response to contain Id and Name. Role should be ignored.
Problem
When I use the JsonIgnore attribute from JSON.NET, the property is ignored entirely. It is not serialized for my response, but the prop of my User is null, when I post the JSON User to my endpoint.
Is there a way to ignore a prop only for serialization?
Thank you in advance!

That's exactly what Data Transfer Objects are for. You should create different DTOs for different purposes (GET/POST).

Use this and pass null when you want to ignore it!
[JsonProperty("property_name", NullValueHandling=NullValueHandling.Ignore)]

Related

How to ignore a property on Swagger body input but still show on responses?

I am using Swashbuckle and Swagger UI to automatically generate API documentation.
My person endpoint uses the following PersonViewmodel:
public int? ID { get; set; }
public string Name { get; set; }
My endpoint method takes in a PersonViewmodel as the body and returns a PersonViewmodel using an IActionResult. However, I don't want the user defining an ID as that is generated by the business logic. In the code if an ID is set it is ignored.
How can I change the Swagger UI to not show the ID in the Example value for the body input but still show the ID for the Example value for the responses?
I have found many ways to remove properties completely from Swagger UI such as [JsonIgnore] or setting the property to internal or private. But how can I remove a property from the input example in swagger but keep it in the output/responses example?
There is a better way now with now. There are two things you need to do
Annotate with SwaggerSchema found in Swashbuckle.AspNetCore.Annotations;
[SwaggerSchema(ReadOnly = true)]
public int Id { get; set; }
EnableAnnotations in AddSwaggerGen
builder.Services.AddSwaggerGen(options =>
{
options.EnableAnnotations();
});
See more about it in the Documentation
Please use the attribute [BindNever] above the property like
[BindNever]
public int? Id {get;set;}
Also, please Check if you're using Newtonsoft.Json to serialize, that could be the reason your System.Text's JsonIgnore attribute didn't work.

Customize serialization to JSON to leave some fields

I have a .NET Core API project backed by MongoDB.
I have a sample model class:
public class MyModel
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
[BsonElement("aField")]
public string AField { get; set; }
[BsonElement("hiddenField")]
public string HiddenField { get; set; }
}
What I want to achieve is to set the HiddenField to null every time when it is returned as the payload in my controller. I am thinking of a custom JSON serializer but don't know how to do it. Can anyone help?
NB: I don't want to manually set the HiddenField to null in every single controller or action.
Apply JsonIgnore attribute to HiddenField property.
public class MyModel
{
// ... other members
[JsonIgnore]
[BsonElement("hiddenField")]
public string HiddenField { get; set; }
}
Since under the hood ASP.Net Core uses Newtonsoft.Json (aka Json.NET) library to de/serialize JSON payloads, you can control serialization and deserialization with the attributes from that library.
Note that JsonIgnore attribute works in both directions: if a client sends hiddenField to the server, the controller won't populate it in the model.
It also worth noting that starting with ASP.NET Core 3.0, Json.NET won't necessarily be the default mechanism of JSON serialization (see this announcement): in the 3.x versions of ASP.NET Core, one must ensure that integration with Json.NET is plugged in, for current solution to work.
Regarding the architectural discussion in the OP comments, it all depends. Of course, mixing such attributes as JsonIgnore and BsonElement in one class means coupling of two separate concerns: service API and data persistence. However, in small and simple applications, proper separation of concerns might be an overkill, and the most straightforward approach might be a better fit.
I find it easier to create a ViewModel (I tend to use ViewModel as a definition of any model I expose, either by sending it to a server side presentation layer or any external resource REST/etc via JSON/XML/etc).
In this instance, I'd create:
public class MyModelVM
{
public string Id { get; set; }
public string AField { get; set; }
public string HiddenField { get; }
}
HiddenField has no set, so it will always be null. Then set your serialization options and return your JSON.
var myModelVM = new MyModelVM(); // however you want to create/map the values
var json = JsonConvert.SerializeObject(myModelVM,
Newtonsoft.Json.Formatting.None,
new JsonSerializerSettings {
NullValueHandling = NullValueHandling.Include
});
If you didn't map values to MyModelVM, then the resulting JSON would look like:
{
"Id" : null,
"AField" : null,
"HiddenField" : null,
}

WebApi Controller model serialization issue

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)));

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.

Categories

Resources