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.