TIPS-JSON日期格式實戰小技巧
1 |
過去介紹過微軟針對DateTime制訂的獨有JSON表示法: "\/Date(…)\/"。今天實際應用時,發現一個有趣現象: ASP.NET Server傳來包含DateTimeJSON字串,因使用JavaScriptSerializer解析,日期會呈現"\/Date(…)\/"格式;在Client端以JSON.parse()還原回成物件,由於未應用到日期值,故未另外將其轉換成JavaScript Date型別,在JavaScript物件中該值維持字串型別。稍後再次以JSON.stringify()成JSON字串傳至ASP.NET Server端解析時,卻出現了"/Date(1330444800000)/ is not a valid value for DateTime. "錯誤訊息。
明明是JavaScriptSerializer.Serialize()出的內容,再送回去給JavaScriptSerializer.Deserialize()卻解不回來,WTF?
用範例來重現問題:
<%@ Page Language="C#" %>
<!DOCTYPE>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
string mode = Request["mode"];
if (!string.IsNullOrEmpty(mode))
{
var jss =
new System.Web.Script.Serialization.JavaScriptSerializer();
if (mode == "get")
{
List<DateTime> list = new List<DateTime>();
list.Add(new DateTime(2012, 2, 29));
Response.Write(jss.Serialize(list));
}
else if (mode == "post")
{
try
{
List<DateTime> list =
jss.Deserialize<List<DateTime>>(Request["json"]);
Response.Write("List[0]=" + list.First());
}
catch (Exception ex)
{
Response.Write("Error: " + ex.Message);
}
}
Response.End();
}
}
</script>
<html>
<head runat="server">
<title>JSON Date Lab</title>
<script type='text/javascript'
src='http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.js'></script>
<script>
$(function () {
$("#btnTest").click(function () {
//取得List<DateTime> JSON
$.get("?mode=get", {}, function (json) {
alert("JSON=" + json);
//將取得的JSON字串傳回去,可解析,但會有時區誤差
$.post("?mode=post", { json: json }, function (r) {
alert("TEST1:" + r);
});
//將JSON字串先解析為物件
var list = JSON.parse(json);
//因JSON.parse()不認得\/Date(...)\/格式,只會被當成字串處理
//而"\/"相當於"\",故最終被解析成字串"/Date(...)/",
var rejson = JSON.stringify(list);
alert("Object->" + rejson);
//將這個字串陣列JSON.stringify()再傳回去,則會因"\/"變成"/"而出現錯誤
$.post("?mode=post", { json: rejson }, function (r) {
alert("TEST2:" + r);
});
});
});
});
</script>
<style>
body,input { font-size: 9pt; }
</style>
</head>
<body>
<form id="form1" runat="server">
<input type="button" id="btnTest" value="Test" />
</form>
</body>
</html>
在Server端建立List<DateTime>,以JavaScriptSerializer.Serialize()後傳至Client端,得到結果如下,符合預期:
JSON=["\/Date(1330444800000)\/"]
直接將剛才接到字串抛回Server端進行JavaScriptSerializer.Deserialize(),可順利取回DateTime,但因時區標準不同會有8小時時差,原本的2012/2/29被視為格林威治時間2/28 16:00。
TEST1:List[0]=2012/2/28 下午 04:00:00
若在Client端使用JSON.parse()轉換,得到的是字串陣列,內建的JSON.parse()不認得"/Date(1330444800000)/",並不會自動將其轉為Date()。
Object->["/Date(1330444800000)/"]
將以上字串陣列JSON.stringify()後再傳給JavaScriptSerializer.Deserialize(),卻發生以下錯誤:
TEST2:Error: /Date(1330444800000)/ is not a valid value for DateTime.
其實問題根源還挺明顯的,JSON.parse()時,依一般JavaScript法則將"\/"視為"/",故解讀出的字串變成"/Date(1330444800000)/";而在JSON.stringify()處理時,"/Date(1330444800000)/"中的正斜線(/)並非必須使用反斜線額外標示的特殊符號,故維持"/Date(1330444800000)/",而非微軟所期望的"\/Date(1330444800000)\/",於是在Server端轉換時便發生錯誤。
一個鋸箭做法是對JSON.stringify()產生的字串進行加工調整,使用RegExp將其中的"/Date(1330444800000)/"再置換回"\/Date(1330444800000)\/",不過考量過,這樣仍會留下8小時時差的問題待解。另一個做法是將"/Date(1330444800000)/"在Client端轉成JavaScript Date()型別,之後用JSON.stringify()會變成"2012-02-28T16:00:00.000Z",而這個格式可被JavaScriptSerializer.Deserialize()正確解讀成DateTime,還可一併解決時區問題,算是較好的解決方案。
以下是程式範例。程式中宣告了一個parseMsJsonDate()可將"/Date(1330444800000)/"轉為Date(),寫法偷學自Kendo UI範例,已是我第二次應用,改寫時還是不禁對JS高人的巧妙寫法讚嘆不已呀...
<script>
//JSON日期轉換
var dateRegExp = /^\/Date\((.*?)\)\/$/;
window.parseMsJsonDate = function (value) {
var date = dateRegExp.exec(value);
return new Date(parseInt(date[1]));
}
$(function () {
$("#btnTest").click(function () {
//取得List<DateTime> JSON
$.get("?mode=get", {}, function (json) {
alert("JSON=" + json);
//將JSON字串先解析為物件
var list = JSON.parse(json);
//將\/Date(...)\/解析成Javascript日期型別
for (var i = 0; i < list.length; i++)
list[i] = parseMsJsonDate(list[i]);
var rejson = JSON.stringify(list);
$.post("?mode=post", { json: rejson }, function (r) {
//正確顯示時間,連時區差問題也一併解決
alert(r);
});
});
});
});
</script>
執行時,兩次alert結果如下:
JSON=["\/Date(1330444800000)\/"]
List[0]=2012/2/29 上午 12:00:00
Comments
# by william0657
最近在學 jQuery + json 看了黑暗大的文章,受益良多,感謝分享!! 心得:這篇又讓我學到一個縮寫 【WTF】