WebAPI, JSON.Net and losing decimal precision - c#

I've come across a bit of a strange issue using WebAPI and JSON.Net. When de-serialising JSON that has been submitted to my API I seem to be losing precision! I'm submitting the decimal to 3 decimal places, but when the values materialises in my object it's only to 2 decimal places!
The JSON I submit looks like this:
{
id: 1,
name: 'mock data',
value: 123.456
}
This is bound to a class that looks something like this:
public class MockObject {
public int Id { get; set; }
public string Name { get; set; }
public decimal Value { get; set; }
}
Just for completeness this is basically what my WebAPI method looks like:
public HttpResponseMessage Post (MockObject data) {
// do something with the value here and return the relevant response
}
I'm submitting the data via a JQuery ajax request, but I can see the posted values are exactly as I expect when inspecting the values in the chrome dev tools before submitting and in fiddler once they've gone "over the wire".
When it gets to doing something with the materialised object in the Post method the value of "Value" is 123.45.
If I submit 2 or fewer decimal places (i.e. 123.4 or 123.45) the value gets de-serialised as expected, however if I submit more than 2 decimal places (i.e. 123.456 or 123.4567 etc the value is always getting de-serialised to 123.45.
Anyone else come across this issue? Any suggestions?

I managed to sort this out.
In the end the problem was being caused by the fact that the culture was being set which contains currency number formatting. The currency number format specifies the number of decimal places which should be used for decimal values.
To fix this I now set the WebApi JSON serializer culture to a new instance of CultureInfo.InvariantCulture in Global.ascx.cs like so:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Culture = new CultureInfo(string.Empty) {
NumberFormat = new NumberFormatInfo {
CurrencyDecimalDigits = 5
}
};
This means that decimal values can have anything up to 5 decimal places.

Related

HttpPost a list of items in Swagger

I have a model class like so -
public class Request
{
public List<ProductIDs> ProductIDs { get; set; }
public string Pid { get; set; }
}
In Swagger, it shows up like this -
In the picture, I am sending a value of 32a, but I am suppose to send "32a" or something. My issue is that when I make a request, the pid gets to the controller. But ProductIDs is always 0.
I think it is because you are passing down an object "ProductID" instead of a string. If your ProductID class only has one variable, a string, it might just be simpler to use:
public List<string> ProductIDs { get; set; }
Otherwise if ProductID has multiple variables such as ID, Location, Amount etc. you will need to pass down an object instead of a string. Meaning it will look something like this in the actual swagger entry:
{"ID": "32a", "var2": 421112, "Product Available":false}
Swagger can read that as an object and assign the correct fields. If you just pass down a number or letter like 32a it essentially reads it as an empty or incorrectly formed object.

C# Web API POST parameter FromBody is always null

I've been scouring the web for hours and tried many different solutions also described here on StackOverflow. I know similar questions have been asked before, but none of the answers or comments have worked for me.
The problem: I have a .NET Web API that has a Post-method with some parameters.
One of the parameters is a complex object that is supposed to be read from the body (that is JSON). However, this object is always null.
This is my code:
// POST api/worksheets/post_event/true/false
[Route("post_event/{newWorksheet}/{eindEvent}")]
[HttpPost]
public Event Post(bool newWorksheet, bool eindEvent, [FromBody] Event eventData)
{
return eventData;
}
To be clear: eventData is the object that's always null. The boolean values are read correctly.
The full request body is:
POST http://localhost:5000/api/worksheets/post_event/true/false
Content-Type: application/json
{"Persnr":1011875, "WorksheetId":null, "Projectnr":81445, "Uursoort":8678, "Tijd":{"09-08-2016 9:25"}}
And for reference, this is the Event-class:
public class Event
{
public long Persnr { get; set; }
public int WorksheetId { get; set; }
public int Projectnr { get; set; }
public int Uursoort { get; set; }
public DateTime Tijd { get; set; }
}
Some of the things I've already tried:
Change JSON to different formats (only values, "Event": {} surrounding the actual object, an = in front of the JSON).
Test with just the Event parameter (removing the others as well as in the route)
Add a default ctor to Event.
Remove the [FromBody] tag. If I do this, the Event-object is not null, but all the properties are. Properties can be filled through the URI, but that is not the desired behavior.
According to all solutions and documentation I have read, it should simply work the way I have it displayed above.
What am I missing?
Your json object is invalid. My suggestion is to always run json object written manually through a json parser like this: http://json.parser.online.fr/
"Tijd":{"09-08-2016 9:25"}
should instead be
"Tijd":["09-08-2016 9:25"]
This usually happens when your object can't be deserialized from JSON request.
The best practice would be to make sure that all the request properties can accept null values (make value types properties are nullable). And then you can validate that all needed request properties are provided, or return 400 error if not. This way you at least will be able to understand what request property causes the problem.
The right JSON for this type should be just
{"Persnr":1011875, "WorksheetId":null, "Projectnr":81445, "Uursoort":8678, "Tijd":"09-08-2016 9:25"}
No curly braces for Tijd, because Tijd is plain DateTime property that can be inferred from string, representing DateTime

MVC3 Range attribute - doesnt admit value zero

I have the following attribute on my field:
[Range(-1,200)]
public decimal MyValue{ get; set; }
If I enter any value that doesn't fall in the range I get:
The field must be between -1 and 200
This is fine.
Here's the problem, I'm getting "The field must be a number" validation message when I enter zero which is a valid value.
Any suggestions?
Thanks
I think this may be happening due to the decimal type. Try this:
[Range(typeof(Decimal),"-1", "200")]
public decimal MyValue{ get; set; }
Source.

Problem getting the MVC3 model binder to bind to a decimal instead of an int

I have a AJAX call from my webpage sending the following data to my MVC3 action method.
{
"name":"Test",
"newitems":[
{"id":15,"amount":100,"unit":"gram"},
{"id":1,"amount":75,"unit":"gram"},
{"id":46,"amount":25,"unit":"gram"}
]
}
In my controller I have the following classes:
public class NewDataItem
{
public string name { get; set; }
public List<NewDataItemDetails> newitems { get; set; }
}
public class NewDataItemDetails
{
public int id { get; set; }
public int amount { get; set; }
public string unit { get; set; }
}
And the Action method that received the request have a NewDataItem as a parameter. This works perfect, however the amount property of NewDataItemDetails might not always contain an int. It might for example be 50.45. So because of that I changed the line public int amount { get; set; } to public decimal amount { get; set; }.
After this change amount is always shown as 0, and not the proper value that it did when it was an int.
Why does MVC fail to bind the value to the property when it is a decimal, when it worked just fine as an int?
See the answer here Default ASP.NET MVC 3 model binder doesn't bind decimal properties from ryudice about creating a DecimalModelBinder. It works and it also keeps all the model validation working, (some other solutions use deserialization which can stop validation from working.
I found the problem.
If I change the line "amount":100, to "amount":"100", it works fine. It seems that the MVC ModelBinder can manage the string to decimal conversion, but not the int to decimal.
Your type (class) NewDataItemDetails property amount is of type int.
You are making things difficult to manage. If you are expecting to have amount as a decimal eventually, it simplifies to actually use amount's type as decimal.
This will avoid having you to extend the default model binder and cater for your behaviour.
I think, it is wiser and it simplies to send the amount as decimal in the ajax call. it provides consistency.
I know I am a bit late with this but there is another solution which works without changing anything until MVC fixes it (I believe it is at their end).
In my javascript I add 0.00001 to my value if it is supposed to be a decimal but is a round number. For those I know should be currency values I know this has no effect and will round down. For those that I do not know their value and this could affect it I remove the 0.00001 from the value after binding in mvc. The binding works correctly as this is no longer an int in the eyes of mvc
If you don't want to change model binder then you can create json object and convert amount to string.It will work fine for me.
var test={"amount":""};
var test1={"amount":100}
test.amount=test1.amount.toString();

asp.net mvc - DTO not picking up incorrect input

I have a basic DTO object which I am trying to update, I noticed if I post some data from the UI to the controller and I enter string inside a decimal field the data annotations validation does not pick this up, in fact the string is converted into 0 for some reason...
How do I get my decimal values to remain decimal i.e. throw an error if a string is added, do I need to create a custom value provider for this DTO object?
My DTO:
public class FeesDTO
{
public int ID{ get; set; }
//[DataType( DataType.Currency)]
//[DisplayFormat(ApplyFormatInEditMode=true)]
public decimal ClientFee { get; set; }
public string VAT { get; set; }
public string GrossProfit { get; set; }
}
If I want to update my fees and I enter 'something' inside the ClientFee field this turns the string input into 0...
NOTE:
The commented out data annotations did not work... Is this the correct way to do this?
when you enter a string value e.g "xyz" for ClientFee which is a decimal variable, it can not be converted to decimal and you will get 0 as value of clientfee but when form is rendered again you will see validation message telling you that
xyz is not valid for field clientfee
this is perhaps due to the fact that when modelbinder fail to convert xyz to decimal it adds the error in Modelstate dictionary's error collection. you can make this value string in your viewmodel and validate it yourself using regex and when you are to store it to db you can convert it to your entity with AutoMapper to similar utility.
I think you need to be using the Data Annotation validators:
http://www.asp.net/mvc/tutorials/validation-with-the-data-annotation-validators-cs
Implement one on the Decimal field that throws a validation error if the Control is posted to with a non decimal entry.

Categories