Asp.net mvc MVC3 RTM在反序列化JSON时无法执行数字类型强制
简单地说,将数据序列化为“application/json;charset=utf-8”格式在MVC3(可能还有更早的版本)中会出现错误。发生的情况是,一个可为null的数字都以null结尾,“decimal”类型的数字在javascript对象(到JSON)中序列化并保留为数字而不是字符串时以0结尾 下面是说明这种错误行为的示例代码Asp.net mvc MVC3 RTM在反序列化JSON时无法执行数字类型强制,asp.net-mvc,model-view-controller,c#-4.0,asp.net-mvc-3,Asp.net Mvc,Model View Controller,C# 4.0,Asp.net Mvc 3,简单地说,将数据序列化为“application/json;charset=utf-8”格式在MVC3(可能还有更早的版本)中会出现错误。发生的情况是,一个可为null的数字都以null结尾,“decimal”类型的数字在javascript对象(到JSON)中序列化并保留为数字而不是字符串时以0结尾 下面是说明这种错误行为的示例代码 --此示例是使用jquery-1.4.4.js和jquery.json-2.2.js创建的----- HomeController.cs: Index.cshtm
--此示例是使用jquery-1.4.4.js和jquery.json-2.2.js创建的----- HomeController.cs: Index.cshtml:
@{
ViewBag.Title=“Index”;
}
指数
@ViewBag.SaveUrl
整数
滴答
十进制数(xx.0)
滴答
十进制数(xx.5)
滴答
整数作为字符串
滴答
十进制数字作为字符串(xx.5)
滴答
$(函数(){
var saveUrl='@ViewBag.saveUrl';
var printObj=函数(inObj,destx){
var dest=$('').appendTo(destx),
dst1=$('')。附件(目的地),
dst2=$('')。附件(目的地);
用于(inObj中的var p){
$('',{text:p,css:{color:'red',padding:'3px',background:'dededede'});
$('',{text:inObj[p]| | null'}).appendTo(dst2);
}
};
$('button.a')。单击(函数(){
var curr=$(this).next(),
输出={
双人:12,
DoubleNull:13,
小数点:14,
小数点空:15,
二比十六,,
Double2Null:17,
小数点2:18,
小数点2整数:19,
单曲:20,,
SingleNull:21,
浮动:22,
零时23分,
国际:24,
IntNull:25,
Int64:26,
Int64Null:27
};
$(“
”)。附加到(当前);
printObj(输出,当前);
$.ajax({
键入:“POST”,
url:saveUrl,
contentType:“应用程序/json;字符集=utf-8”,
数据类型:“json”,
数据:$.toJSON({
检查表单:“fbde6eda-dde6-4ba9-b82d-3a35349415f0”,
结果:outR
}),
错误:函数(jqXHR、textStatus、errorshown){
警报(“保存失败”);
},
成功:函数(数据、文本状态、jqXHR){
printObj(数据,货币);
}
});
});
$('button.b')。单击(函数(){
var curr=$(this).next(),
输出={
双倍:12.0,
DoubleNull:13.0,
十进制:14.0,
小数点空:15.0,
双打2:16.0,
Double2Null:17.0,
小数点2:18.0,
小数点2整数:19.0,
单曲:20.0,
SingleNull:21.0,
浮动:22.0,
FloatNull:23.0,
国际:24.0,
IntNull:25.0,
Int64:26.0,
Int64Null:27.0
};
$(“
”)。附加到(当前);
printObj(输出,当前);
$.ajax({
键入:“POST”,
url:saveUrl,
contentType:“应用程序/json;字符集=utf-8”,
数据类型:“json”,
数据:$.toJSON({
检查表单:“fbde6eda-dde6-4ba9-b82d-3a35349415f0”,
结果:outR
}),
错误:函数(jqXHR、textStatus、errorshown){
警报(“保存失败”);
},
成功:函数(数据、文本状态、jqXHR){
printObj(数据,货币);
}
});
});
$('button.c')。单击(函数(){
var curr=$(this).next(),
输出={
双倍:12.5,
DoubleNull:13.5,
小数点:14.5,
小数点零:15.5,
双2:16.5,,
Double2Null:17.5,
小数点2:18.5,
小数点2整数:19.5,
单曲:20.5,
SingleNull:21.5,
浮动:22.5,
浮动空值:23.5,
国际:24.5,
IntNull:25.5,
Int64:26.5,
Int64Null:27.5
};
$(“
”)。附加到(当前);
printObj(输出,当前);
$.ajax({
键入:“POST”,
url:saveUrl,
contentType:“应用程序/json;字符集=utf-8”,
数据类型:“json”,
数据:$.toJSON({
“检查表单”:“fbde6eda-dde6-4ba9-b82d-3a35349415f0”,
“结果”:outR
}),
错误:函数(jqXHR、textStatus、errorshown){
警报(“保存失败”);
},
成功:函数(数据、文本状态、jqXHR){
printObj(数据,货币);
}
});
});
$('button.d')。单击(函数(){
var curr=$(this).next(),
输出={
双:“12”,
DoubleNull:'13',
十进制:“14”,
小数为空:“15”,
双二:'十六',,
Double2Null:'17',
小数点2:'18',
小数点2整数:“19”,
单曲:'20',
硅
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; }
}
}
@{
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>
$.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);
}
});
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;
}
}