Truly
写精彩代码 品暇逸人生

作者:Truly
日期:2007.7.29

如我前面文章介绍的那样,JSON在现代JavaScript编程中占据了重要位置,如果你阅读过大型的类库代码,例如AjaxPro,MS ASP.NET AJAX等,你会发现,在组织和交互数据和都普遍采用了JSON方式。我前面那篇文章《设计模式在JavaScript中的应用(1)》中,使用了较为简单的数据映射,为了避免时区的差别,我将日期用JSON简单表示为如下格式:

"CreateDate":new Date('Wed, 25 Jul 2007 09:59:00 GMT+8')

这样的日期表示在小型应用中具有简单,可读性高的优点,但是对于大型应用程序而言,更好的组织数据,压缩数据,减少通信量则更为重要。今天我们探讨一下微软ASP.NET AJAX中日期类型的JSON处理。

首先看一下ASP.NET AJAX服务器端对日期类型JSON序列化的处理:(详见Ajax扩展源码中的JavaScriptSerializer.cs

internal static readonly long DatetimeMinTimeTicks = (new DateTime(197011000, DateTimeKind.Utc)).Ticks;
private static void SerializeDateTime(DateTime datetime, StringBuilder sb) {
            
        sb.Append(
@"""\/Date(");
        sb.Append((datetime.ToUniversalTime().Ticks 
- DatetimeMinTimeTicks) / 10000);
        sb.Append(
@")\/""");
}

最早期,微软使用下面方法进行序列化:

ASP.NEXT 中的 AJAX JSON 序列化程序将 DateTime 实例编码为 JSON 字符串。在预发布周期中,ASP.NET AJAX 使用格式“@ticks@”,其中 ticks 表示从通用协调时间 (UTC) 1970 年 1 月 1 日起经过的毫秒数。以 UTC 表示的日期和时间(如 1989 年 11 月 29 日 4:55:30 AM)会编码为“@62831853071@”。虽然简单易懂,但此格式无法区分序列化的日期和时间值与看起来像序列化日期但又不需要进行反序列化的值。因此,ASP.NET AJAX 团队对最终版本进行了更改,通过采用“\/Date(ticks)\/”格式解决了这一问题。

新格式借助一个小技巧减少了误解的可能性。在 JSON 中,字符串中的正斜杠 (/) 字符可以用反斜杠 (\) 进行转义(即使没有对此进行严格要求)。ASP.NET AJAX 团队利用这点修改了 JavaScriptSerializer,将 DateTime 实例编写为字符串“\/Date(ticks)\/”。两个正斜杠的转义只是表面的,但对 JavaScriptSerializer 至关重要。按照 JSON 规则,“\/Date(ticks)\/” 在技术上相当于 “/Date(ticks)/”,但 JavaScriptSerializer 会将前者反序列化为 DateTime,将后者反序列化为 String。因此与预发布版本的“@ticks@”格式相比,混淆的可能性会大大减少。

注:此段文字来源于MSDN文章JavaScript 和 .NET 中的 JavaScript Object Notation (JSON) 简介

这里我要补充一个技巧,大家知道,在document中如果出现<script>和</script>那么脚步解释器就会对其中文本进行解析,所以如果你编写下面的代码,则会提示错误:

<script>
document.write(
"<script>alert(1);</script>");
</script>

因为中间出现了“</script>”这样的代码,那么我们可以使用:

<script>
document.write(
"<script>alert(1);</scr"+"ipt>");
</script>

来避免这种错误,这时我们还可以使用“\”来转义:

<script>
document.write(
"<script>alert(1);<\/script>");
</script>



下面我们看ASP.NET AJAX脚本库中对日期的反序列化代码:

Sys.Serialization.JavaScriptSerializer.deserialize = function Sys$Serialization$JavaScriptSerializer$deserialize(data) {
    
/// <param name="data" type="String"></param>
    /// <returns></returns>
    var e = Function._validateParams(arguments, [
        {name: 
"data", type: String}
    ]);
    
if (e) throw e;

    
if (data.length === 0throw Error.argument('data', Sys.Res.cannotDeserializeEmptyString);
                                                            
    
try {    
        
var exp = data.replace(new RegExp('(^|[^\\\\])\\"\\\\/Date\\((-?[0-9]+)\\)\\\\/\\"', 'g'), "$1new Date($2)");
        
return eval('(' + exp + ')');
    }
    
catch (e) {
         
throw Error.argument('data', Sys.Res.cannotDeserializeInvalidJson);
    }
}

我把这个序列化器相关代码整理到Sys.Serialization.js文件中了,有兴趣的可以下载查看。

总结:这样的方式很有参考价值,值得我们在封装数据时借鉴,关于JS对数据的压缩,我前面有一篇简易的作法《压缩JavaScript的宏》,而更加高级的混淆压缩,将留到下一篇文章进行重点研讨。

谢谢阅读。

 


 

posted on 2007-07-29 14:06  Truly  阅读(6012)  评论(2编辑  收藏  举报