很多ASP.NET项目,尤其是使用了Ajax的项目,常常需要返回JSON格式的数据。.NET框架从3.5版本开始提供了JSON的序列化和反序列化工具,不过个人感觉不太好用,后来找了第三方的Newtonsoft.Json来用。再后来,在MVC4中,微软已经默认使用Json.NET(Newtonsoft.Json)来处理JSON数据了。
JavaScript数值精度是32位,如果整数数度超过32位,就会被当作浮点数处理。换句话说,如果从服务端生成的JSON,某个值是64位整数,传到前端JavaScript,再传回服务端,不做任何运算,都可能出现失真。做个实验:
1
2
3
|
>
var
a = 123456789012345678
> console.log(a);
123456789012345680
|
很要命的一点是,数据库设计中常常会用bigint(64位)整数来作为主键,是一个非常重要而且不能有偏差的数据,比如,一个模型:
1
2
3
4
5
|
// C# 匿名对象
new
{
id: 123456789012345678L,
name:
"James"
};
|
转换成JSON输出到前端是:
1
|
{
"id"
:123456789012345678,
"name"
:
"James"
}
|
通过Ajax取得的对象输出就有点不妙了
1
2
3
4
|
$.getJSON(
"/api/test"
).done(
function
(jo) {
console.log(jo);
});
// Object {id: 123456789012345680, name: "James"}
|
显然,这个对象修改数值之后再传回服务器,就会找不到主键,或者更新成错误的数据,造成一个不易发现的巨大BUG。
解决办法当然是有的,JavaScript处理字符串的能力非常强,完全可以把服务器端的64位整数处理成字符串类型。不过 Json.NET 默认是把 long 处理成 number 类型的,如果要处理成 string 类型,需要自定义一个JsonConverter。
考虑到用十六进制表示的整数看起来比较整齐,所以定义一个HexLongConverter来转换long/ulong型数据与16进制表示的字符串。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
public
class
HexLongConverter : JsonConverter
{
public
override
void
WriteJson(JsonWriter writer,
object
value, JsonSerializer serializer)
{
// 由于CanConvert过滤,数据类型只可能是long或ulong
// 统一转换成long类型处理
long
v = value
is
ulong
? (
long
)(
ulong
)value : (
long
)value;
writer.WriteValue(v.ToString(
"X16"
));
}
public
override
object
ReadJson(JsonReader reader, Type objectType,
object
existingValue, JsonSerializer serializer)
{
// 取得读到的十六进制字符串
string
hex = reader.Value
as
string
;
// 调用ToInt64扩展将字符串转换成long型
// ToInt64扩展方法后附
long
v = hex.ToInt64(NumberStyles.HexNumber, 0L);
// 将v转换成实际需要的类型 ulong 或 long(不转换)
return
typeof
(
ulong
) == objectType ? (
object
) (
ulong
) v : v;
}
public
override
bool
CanConvert(Type objectType)
{
// 只处理long和ulong两种类型的数据
switch
(objectType.FullName)
{
case
"System.Int64"
:
case
"System.UInt64"
:
return
true
;
default
:
return
false
;
}
}
}
|
上面的代码用到了一个string的扩展方法ToInt32:
1
2
3
4
5
6
7
8
9
|
public
static
class
StringExtention
{
public
static
int
ToInt32(
this
string
me, NumberStyles style,
int
defaultValue)
{
int
? value = me.ToInt32(style);
return
value ==
null
? defaultValue : value.Value;
}
}
|
在序列化或反序列化模型的时候,只需要加入HexLongConverter对象作为参数即可:
1
2
3
4
|
// 序列化
string
json = JsonConvert.SerializeObject(model,
new
HexLongConverter());
// 反序列化
SomeModal model = JsonConvert.DeserializeObject<model>(json,
new
HexLongConverter));
|
本文转自边城__ 51CTO博客,原文链接:http://blog.51cto.com/jamesfancy/1409149,如需转载请自行联系原作者