简单地将序列化数据放在MVC3(以及可能的旧版本)中的“application / json; charset = utf-8”格式行为异常中。会发生什么是可以为空的数字都以null结尾,并且当在javascript对象(到JSON)中将它们序列化并将它们保留为数字而不是字符串时,“decimal”类型的数字最终为0。
以下是说明此不当行为的示例代码
- - - 这个例子是使用jquery-1.4.4.js和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>
运行它单击每个按钮一次,然后查看之前/之后。提前感谢您提供解决此问题的任何见解,更正或帮助。
您还可以下载上面列出的代码示例,并在此链接中查看官方错误报告:http://aspnet.codeplex.com/workitem/8114
<小时/> 编辑:我将此图片包含在内,以帮助每个人了解此处发生的事情
click here to see the screenshot of the included example running
基本上:{propertyThatIsADecimal:54}在不同场景中的多个不同数字类型的服务器上成为{propertyThatIsADecimal:0},似乎没有任何押韵或理由。
答案 0 :(得分:3)
原因是因为当MVC遇到一个数字时,它会将其视为Int32
。因此,由于某些原因,没有转换器从Int32
说Decimal
或Nullable<Int64>
。围绕这个问题有几种方法。字符串,就像您在项目中已有或创建自定义模型绑定器一样。
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;
}
}
我不完全确定为什么我们仍然无法从Int32
转换为Decimal
,但问题存在于MVC的ValueProviderResult.ConvertSimpleType
中。它正在使用TypeDescriptor.GetConverter(your propertyType)
,并且没有针对这些类型的转化。
我不喜欢这种特殊的方法,但它是目前唯一可用的方法。
答案 1 :(得分:0)
我只瞥了一眼您的问题,所以如果不适用,我会道歉。但我记得遇到过Json的一些问题:
答案 2 :(得分:0)
我下载了您的代码并运行了示例。当我单击按钮时,我收到了一个JavaScript错误(Microsoft JScript运行时错误:对象不支持此属性或方法):
$.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);
}
});
我从你的描述中也不清楚“之前/之后”应该是什么。你能用一个例子将它提炼成绝对最简单的情况吗?我假设如果我们能够解决这个问题,那么它将适用于其余的情况。
答案 3 :(得分:0)
我正在反对同样的问题,但不幸的是,我不可能在序列化之前进行任何类型的转换。我还没有找到解决方案。
此外,它不是只是可以为我反序列化的可空类型和小数 - 一些字符串为null,并且子实体数组上的每个属性也显示为空。
我怀疑它可能是对实体框架POCO代理类进行反序列化的问题,但在看到您的代码后,这显然不是问题。