MVC3 RTM does not allow forced numeric when deserializing JSON

Simply put, serializing data in the format "application / json; charset = utf-8" does not work correctly in MVC3 (and possibly in older versions). What happens is a zero-number number ending in zero, and decimal-type numbers end in 0 when they are serialized inside the javascript object (before JSON) and leave them as numbers, not strings.

Here is an example code that illustrates this incorrect behavior.
- - - this example was created using jquery-1.4.4.js and jquery.json-2.2.js - - - -

HomeController.cs:

public class HomeController : Controller { public ActionResult Index() { ViewBag.SaveUrl = Url.Action("Save", "Home", new { inspectionFormID = Guid.Empty }, Request.Url.Scheme); return View(); } public JsonResult Save(Guid inspectionFormID, JsonTest result) { return Json(result); } public class JsonTest { public double Double { get; set; } public double? DoubleNull { get; set; } public decimal Decimal { get; set; } public decimal? DecimalNull { get; set; } public Double Double2 { get; set; } public Double? Double2Null { get; set; } public Decimal Decimal2 { get; set; } public Decimal? Decimal2Null { get; set; } public Single Single { get; set; } public Single? SingleNull { get; set; } public float Float { get; set; } public float? FloatNull { get; set; } public int Int { get; set; } public int? IntNull { get; set; } public Int64 Int64 { get; set; } public Int64? Int64Null { get; set; } } } 

Index.cshtml:

  @{ ViewBag.Title = "Index"; } <h2>Index</h2> <b>@ViewBag.SaveUrl</b> <br /> <hr /> <br /> <h3>Integral Numbers</h3> <button type="button" class="a">Clicky</button> <div></div> <h3>Decimal Numbers (xx.0)</h3> <button type="button" class="b">Clicky</button> <div></div> <h3>Decimal Numbers (xx.5)</h3> <button type="button" class="c">Clicky</button> <div></div> <h3>Integral Numbers as strings</h3> <button type="button" class="d">Clicky</button> <div></div> <h3>Decimal Numbers as strings (xx.5)</h3> <button type="button" class="e">Clicky</button> <div></div> <script type="text/javascript"> $(function () { var saveUrl = '@ViewBag.SaveUrl'; var printObj = function (inObj, destx) { var dest = $('<table>').appendTo(destx), dst1 = $('<tr>').appendTo(dest), dst2 = $('<tr>').appendTo(dest); for (var p in inObj) { $('<th>', { text: p, css: { color: 'red', padding: '3px', background: '#dedede' } }).appendTo(dst1); $('<td>', { text: inObj[p] || 'null' }).appendTo(dst2); } }; $('button.a').click(function () { var curr = $(this).next(), outR = { Double: 12, DoubleNull: 13, Decimal: 14, DecimalNull: 15, Double2: 16, Double2Null: 17, Decimal2: 18, Decimal2Null: 19, Single: 20, SingleNull: 21, Float: 22, FloatNull: 23, Int: 24, IntNull: 25, Int64: 26, Int64Null: 27 }; $('<hr />').appendTo(curr); printObj(outR, curr); $.ajax({ type: 'POST', url: saveUrl, contentType: "application/json; charset=utf-8", dataType: 'json', data: $.toJSON({ inspectionFormID: 'fbde6eda-dde6-4ba9-b82d-3a35349415f0', result: outR }), error: function (jqXHR, textStatus, errorThrown) { alert('save failed'); }, success: function (data, textStatus, jqXHR) { printObj(data, curr); } }); }); $('button.b').click(function () { var curr = $(this).next(), outR = { Double: 12.0, DoubleNull: 13.0, Decimal: 14.0, DecimalNull: 15.0, Double2: 16.0, Double2Null: 17.0, Decimal2: 18.0, Decimal2Null: 19.0, Single: 20.0, SingleNull: 21.0, Float: 22.0, FloatNull: 23.0, Int: 24.0, IntNull: 25.0, Int64: 26.0, Int64Null: 27.0 }; $('<hr />').appendTo(curr); printObj(outR, curr); $.ajax({ type: 'POST', url: saveUrl, contentType: "application/json; charset=utf-8", dataType: 'json', data: $.toJSON({ inspectionFormID: 'fbde6eda-dde6-4ba9-b82d-3a35349415f0', result: outR }), error: function (jqXHR, textStatus, errorThrown) { alert('save failed'); }, success: function (data, textStatus, jqXHR) { printObj(data, curr); } }); }); $('button.c').click(function () { var curr = $(this).next(), outR = { Double: 12.5, DoubleNull: 13.5, Decimal: 14.5, DecimalNull: 15.5, Double2: 16.5, Double2Null: 17.5, Decimal2: 18.5, Decimal2Null: 19.5, Single: 20.5, SingleNull: 21.5, Float: 22.5, FloatNull: 23.5, Int: 24.5, IntNull: 25.5, Int64: 26.5, Int64Null: 27.5 }; $('<hr />').appendTo(curr); printObj(outR, curr); $.ajax({ type: 'POST', url: saveUrl, contentType: "application/json; charset=utf-8", dataType: 'json', data: $.toJSON({ 'inspectionFormID': 'fbde6eda-dde6-4ba9-b82d-3a35349415f0', 'result': outR }), error: function (jqXHR, textStatus, errorThrown) { alert('save failed'); }, success: function (data, textStatus, jqXHR) { printObj(data, curr); } }); }); $('button.d').click(function () { var curr = $(this).next(), outR = { Double: '12', DoubleNull: '13', Decimal: '14', DecimalNull: '15', Double2: '16', Double2Null: '17', Decimal2: '18', Decimal2Null: '19', Single: '20', SingleNull: '21', Float: '22', FloatNull: '23', Int: '24', IntNull: '25', Int64: '26', Int64Null: '27' }; $('<hr />').appendTo(curr); printObj(outR, curr); $.ajax({ type: 'POST', url: saveUrl, contentType: "application/json; charset=utf-8", dataType: 'json', data: $.toJSON({ 'inspectionFormID': 'fbde6eda-dde6-4ba9-b82d-3a35349415f0', 'result': outR }), error: function (jqXHR, textStatus, errorThrown) { alert('save failed'); }, success: function (data, textStatus, jqXHR) { printObj(data, curr); } }); }); $('button.e').click(function () { var curr = $(this).next(), outR = { Double: '12.5', DoubleNull: '13.5', Decimal: '14.5', DecimalNull: '15.5', Double2: '16.5', Double2Null: '17.5', Decimal2: '18.5', Decimal2Null: '19.5', Single: '20.5', SingleNull: '21.5', Float: '22.5', FloatNull: '23.5', Int: '24.5', IntNull: '25.5', Int64: '26.5', Int64Null: '27.5' }; $('<hr />').appendTo(curr); printObj(outR, curr); $.ajax({ type: 'POST', url: saveUrl, contentType: "application/json; charset=utf-8", dataType: 'json', data: $.toJSON({ 'inspectionFormID': 'fbde6eda-dde6-4ba9-b82d-3a35349415f0', 'result': outR }), error: function (jqXHR, textStatus, errorThrown) { alert('save failed'); }, success: function (data, textStatus, jqXHR) { printObj(data, curr); } }); }); }); </script> 

Launch it by clicking each button once and look at the before / after. Thanks in advance for any information, fix, or help you can provide to resolve this issue.

You can also download the code samples listed above and see the official error report at this link: http://aspnet.codeplex.com/workitem/8114


EDIT: I am including this image to help everyone understand what is going on here.

Click here to see a screenshot of the attached example .

Basically: {propertyThatIsADecimal: 54} becomes {propertyThatIsADecimal: 0} on the server for several different types of numbers in different scenarios that seem to have no rhyme or reason.

+4
source share
4 answers

The reason is that when MVC encounters a number, it treats it as Int32 . For some reason there are no converters for some reason from Int32 to say a Decimal or Nullable<Int64> . There are a couple of ways to solve this problem. Strings, as you already have in your project, or create a custom mediator.

 public class JsonTestModelBinder : IModelBinder { public virtual object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { JsonTest result = new JsonTest(); foreach (var property in typeof(JsonTest).GetProperties()) { //the value provider starts with the name of the property we're binding to //i'm not sure if this changed or not as i don't recall having to do this //before - you can remove "result." if your needs don't require it var value = bindingContext.ValueProvider.GetValue("result." + property.Name); if (value != null && value.RawValue != null) { //are we binding to a nullable? if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) { property.SetValue(result, Convert.ChangeType(value.AttemptedValue, new NullableConverter(property.PropertyType).UnderlyingType), null); } else { property.SetValue(result, Convert.ChangeType(value.AttemptedValue, property.PropertyType), null); } } } return result; } } 

I'm not quite sure why we still cannot convert from Int32 to Decimal , but the problem exists in MVC ValueProviderResult.ConvertSimpleType . It uses TypeDescriptor.GetConverter(your propertyType) , and there are no conversions for these types.

I do not like this particular method, but it is the only one available to you now.

+3
source

I just looked at your question, so I apologize if this does not apply. But I remember that I encountered several problems with Json:

  • Was this a mandatory issue? If so, perhaps you can implement a custom binding. (Apparently MVC3 already uses JsonFactory).
  • I cannot remember the problem, but I needed to add JsonRequestBehaviour.AllowGet when calling jQuery using .getJSON.
  • Does it matter that you do not have [HttpGet] or [HttpPost] in the save method?
0
source

I downloaded your code and ran the example. When I clicked on the button, I received a JavaScript error (Microsoft JScript runtime error: the object does not support this property or method):

 $.ajax({ type: 'POST', url: saveUrl, contentType: "application/json; charset=utf-8", dataType: 'json', data: $.toJSON({ inspectionFormID: 'fbde6eda-dde6-4ba9-b82d-3a35349415f0', result: outR }), error: function (jqXHR, textStatus, errorThrown) { alert('save failed'); }, success: function (data, textStatus, jqXHR) { printObj(data, curr); } }); 

It was also not clear to me from your description what should have been "before / after." Can you separate this to the absolute simplest case with just one example? I assume that if we can understand this in this case, this applies to the rest.

0
source

I struggle against the same problem, but, unfortunately, it is not possible for me to make any transformations before serialization. I have yet to find a solution.

In addition, these are not only null types and decimals that are not deserialized for me - a couple of lines are null, and each individual property in the array of child objects is also displayed as null.

I suspected this might be a deserialization issue for the Entity Framework POCO proxy class, but this is clearly not a problem after looking at your code.

0
source

Source: https://habr.com/ru/post/1340373/


All Articles