QuirksBlog:
The AJAX response: XML, HTML, or JSON?
早先這篇文章在TSS上貼出的時候,我很快的瀏覽,便一眼看出這篇文章作者所處的角度。事實上,AJAX概念的不完整和不嚴(yán)密性——異步的JavaScript + XML——導(dǎo)致作者將AJAX的輸出分為三種類型:XML, HTML片斷和JSON對象字符串。
首先看XML。對于RPC的數(shù)據(jù)傳輸,XML從來都是當(dāng)仁不二的選擇。對于將對象序列化為XML字符串的方式,往往有兩種選擇,一種是將對象本身的屬性作為節(jié)點進(jìn)行輸出,一種是利用語言的元數(shù)據(jù)特性進(jìn)行序列化輸出。兩者存在較大不同。對于第一種,輸出案例如下:
而對于第二種,輸出案例如下:
前一種一般來說是同一進(jìn)程內(nèi)(同一JVM內(nèi))對對象進(jìn)行序列化和反序列化的方法,在XML-Java綁定一類的框架中比較多見,例如XStream, JiBX, Castor等等。當(dāng)同一進(jìn)程內(nèi)能夠找到對象具備的正確類型時,這種序列化/反序列化設(shè)計和實現(xiàn)都不太困難,而且針對空值處理也比較容易。
可以看出,由于這種方式成本較低,有大量成熟的開源庫可用,加上在AJAX中的X(ML)的“理論”指導(dǎo)下,許多開發(fā)者采用這種方式將對象輸出給前臺瀏覽 器,然后利用瀏覽器的XML能力進(jìn)行輸出顯示。從那篇文章中可以看出,這種工作是純粹的dirty work, 并且由于領(lǐng)域模型或者DTO的不同,輸出的xml結(jié)構(gòu)含義也不同,在對空值的處理上更是麻煩,在處理顯示邏輯的時候,基本上很難在客戶端對以這種方式傳遞 的XML以一種統(tǒng)一的方式進(jìn)行還原。以這種方式來進(jìn)行AJAX開發(fā),小規(guī)模應(yīng)用還不能顯出弊端,但是大規(guī)模應(yīng)用出現(xiàn),領(lǐng)域?qū)ο筝^多時,必然出現(xiàn)怨言。而我 們使用AJAX的真實意圖并非來無趣地去解析各種各樣的XML,而是需要XML中代表的數(shù)據(jù)。至于XML是什么樣子,除了調(diào)試階段,沒有人會關(guān)心。
第二種XML的序列化方式是絕大多數(shù)跨進(jìn)程遠(yuǎn)程調(diào)用協(xié)議/框架所采取的方式。SOAP/WebService如此,XMLRPC如此,Buffalo采用 的Burlap也如此。這種遠(yuǎn)程調(diào)用的特征是,在協(xié)議約定的條件下,調(diào)用方和被調(diào)用方需要保證數(shù)據(jù)語義的完整性。拿什么保證?就是數(shù)據(jù)定義信息了。這些協(xié) 議的共同特征是采用謀一些標(biāo)記來描述數(shù)據(jù)類型:int, long, float, string, list...這樣定義完成后,只要根據(jù)協(xié)議,任何語言都能以一種通用的方式對數(shù)據(jù)進(jìn)行還原。AJAX引擎的概念也就漸漸呈現(xiàn)——通過某一種協(xié)議,他就處 于中間的位置,負(fù)責(zé)將調(diào)用方的請求包裝為協(xié)議,轉(zhuǎn)化為執(zhí)行方能夠理解的動作加以執(zhí)行;然后將執(zhí)行結(jié)果轉(zhuǎn)化為協(xié)議的值,傳遞給客戶端,客戶端引擎將協(xié)議內(nèi)容 解析為能夠理解的對象結(jié)構(gòu)傳遞給客戶端,然后就可以利用這個數(shù)據(jù)來顯示了。XML-RPC如此,WebService如此,DWR如此,JSON如此, Buffalo也如此。
綜上所述,純粹使用領(lǐng)域模型特定的輸出XML傳遞給客戶端是一種相當(dāng)原始的做法。他只在很低的層次上印證了所謂AJAX的概念。然而,這種概念的深入思維結(jié)果便是一個AJAX引擎。
文中提到的第二種輸出方式:HTML——應(yīng)該被看作WEB的一個特例,應(yīng)該說這是歷史因素、瀏覽器能力、設(shè)計者等多方因素達(dá)到的一個平衡。許多歷史應(yīng)用 中,大多采用將頁面進(jìn)行一定規(guī)則的分割,然后include或者jsp 2.0 tagfile的方式對公共區(qū)域進(jìn)行處理,留下一小部分進(jìn)行動態(tài)顯示。這里舉一個例子:查詢,顯示書籍列表。傳統(tǒng)做法就是上面一個搜索條件輸入文本框,下 面是搜索結(jié)果列表,處于同一個頁面。原來的搜索方式每次提交都要刷新整個頁面,用戶體驗不太好,現(xiàn)在需要改進(jìn)。按照激進(jìn)的Ajax做法應(yīng)該是當(dāng)搜索按鈕點 擊時,調(diào)用bookSearchService.search(String terms)的方法,取得結(jié)果列表,然后Ajax引擎處理結(jié)果數(shù)據(jù),將數(shù)據(jù)反序列化為javascript對象,開發(fā)者利用這些對象,要么利用DOM, 要么利用JavaScript Template, 在頁面對搜索結(jié)果進(jìn)行展示。然而,問題出現(xiàn)了:
搜索結(jié)果太多了,用DOM操作速度太慢;
開發(fā)者人手不夠,沒有時間,而這個頁面以前寫過;
那么出現(xiàn)這種情況時,很可能的做法便是,見原來那個搜索結(jié)果頁面刨去其他不相干的部分,留下搜索結(jié)果部分,然后利用一個resultDiv.innerHTML=xmlhttp.responseText完事。這樣做,既達(dá)到了改善體驗的效果,也提高了開發(fā)速度。
輸出HTML另外一種方式文中也沒有提及。事實上,HTML不僅僅作為片斷,更重要的是作為頁面視圖的一部分。在buffalo的demo中,可以看到所 有的頁面都被管理起來了,buffalo接管了視圖的切換;這種設(shè)計也存在于gmail/163新版郵箱中。這個應(yīng)用比上面的HTML片斷的使用方式還要 重要,因為這里是緩存可以介入的地方。通過不同的緩存策略,可以將用戶的實際和心理等待時間大大減少,從而進(jìn)一步改善用戶操作體驗,提升產(chǎn)品競爭力。 (PS. 在Buffalo 1.3中將加入客戶端緩存模塊,無需你動手,buffalo為你緩存視圖)
文中提及的第三種方式,JSON,根據(jù)第一部分的描述,應(yīng)該比較清楚了。實際上他扮演了一個Ajax引擎的角色。這里不得不提的是,使用JSON的相當(dāng)危 險的。因為他的協(xié)議表現(xiàn)與語言本身綁定太死。如果某一天,JSON協(xié)議變化了,那么使用JSON的應(yīng)用基本上不可能應(yīng)對這個變化——因為返回結(jié)果是 eval()得到的。而Buffalo將協(xié)議隱藏起來了,1.2版本甚至連服務(wù)器端的ServiceInvoker都將burlap實現(xiàn)依賴隔離開。現(xiàn)在 使用burlap,也許某一天不用了,buffalo的應(yīng)用照樣可以運行。因為里面的細(xì)節(jié)都是透明的,作為開發(fā)者,你只需要關(guān)注數(shù)據(jù)對象本身,而非用來描 述對象的那一堆字符。
早先這篇文章在TSS上貼出的時候,我很快的瀏覽,便一眼看出這篇文章作者所處的角度。事實上,AJAX概念的不完整和不嚴(yán)密性——異步的JavaScript + XML——導(dǎo)致作者將AJAX的輸出分為三種類型:XML, HTML片斷和JSON對象字符串。
首先看XML。對于RPC的數(shù)據(jù)傳輸,XML從來都是當(dāng)仁不二的選擇。對于將對象序列化為XML字符串的方式,往往有兩種選擇,一種是將對象本身的屬性作為節(jié)點進(jìn)行輸出,一種是利用語言的元數(shù)據(jù)特性進(jìn)行序列化輸出。兩者存在較大不同。對于第一種,輸出案例如下:
<books>
<book>
<title>JavaScript, the Definitive Guide</title>
<publisher>O'Reilly</publisher>
<author>David Flanagan</author>
<cover src="/images/cover_defguide.jpg" />
<blurb>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</blurb>
</book>
<book>
<title>DOM Scripting</title>
<publisher>Friends of Ed</publisher>
<author>Jeremy Keith</author>
<cover src="/images/cover_domscripting.jpg" />
<blurb>Praesent et diam a ligula facilisis venenatis.</blurb>
</book>
</books>
<book>
<title>JavaScript, the Definitive Guide</title>
<publisher>O'Reilly</publisher>
<author>David Flanagan</author>
<cover src="/images/cover_defguide.jpg" />
<blurb>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</blurb>
</book>
<book>
<title>DOM Scripting</title>
<publisher>Friends of Ed</publisher>
<author>Jeremy Keith</author>
<cover src="/images/cover_domscripting.jpg" />
<blurb>Praesent et diam a ligula facilisis venenatis.</blurb>
</book>
</books>
而對于第二種,輸出案例如下:
<list>
<type>java.util.List</type>
<map>
<type>yourapp.domain.Book</type>
<string>title</string>
<string>JavaScript, the Definitive Guide</string>
<string>publisher</string>
<string>O'Reilly</string>
<string>author</string>
<string>David Flanagan</string>
<string>cover</string>
<string>/images/cover_defguide.jpg</string>
<string>blurb</string>
<string>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</string>
</map>
<map>
<type>yourapp.domain.Book</type>
<string>title</string>
<string>DOM Scripting</string>
<string>publisher</string>
<string>Friends of Ed</string>
<string>author</string>
<string>Jeremy Keith</string>
<string>cover</string>
<string>/images/cover_domscripting.jpg</string>
<string>blurb</string>
<string>Praesent et diam a ligula facilisis venenatis.</string>
</map>
</list>
<type>java.util.List</type>
<map>
<type>yourapp.domain.Book</type>
<string>title</string>
<string>JavaScript, the Definitive Guide</string>
<string>publisher</string>
<string>O'Reilly</string>
<string>author</string>
<string>David Flanagan</string>
<string>cover</string>
<string>/images/cover_defguide.jpg</string>
<string>blurb</string>
<string>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</string>
</map>
<map>
<type>yourapp.domain.Book</type>
<string>title</string>
<string>DOM Scripting</string>
<string>publisher</string>
<string>Friends of Ed</string>
<string>author</string>
<string>Jeremy Keith</string>
<string>cover</string>
<string>/images/cover_domscripting.jpg</string>
<string>blurb</string>
<string>Praesent et diam a ligula facilisis venenatis.</string>
</map>
</list>
前一種一般來說是同一進(jìn)程內(nèi)(同一JVM內(nèi))對對象進(jìn)行序列化和反序列化的方法,在XML-Java綁定一類的框架中比較多見,例如XStream, JiBX, Castor等等。當(dāng)同一進(jìn)程內(nèi)能夠找到對象具備的正確類型時,這種序列化/反序列化設(shè)計和實現(xiàn)都不太困難,而且針對空值處理也比較容易。
可以看出,由于這種方式成本較低,有大量成熟的開源庫可用,加上在AJAX中的X(ML)的“理論”指導(dǎo)下,許多開發(fā)者采用這種方式將對象輸出給前臺瀏覽 器,然后利用瀏覽器的XML能力進(jìn)行輸出顯示。從那篇文章中可以看出,這種工作是純粹的dirty work, 并且由于領(lǐng)域模型或者DTO的不同,輸出的xml結(jié)構(gòu)含義也不同,在對空值的處理上更是麻煩,在處理顯示邏輯的時候,基本上很難在客戶端對以這種方式傳遞 的XML以一種統(tǒng)一的方式進(jìn)行還原。以這種方式來進(jìn)行AJAX開發(fā),小規(guī)模應(yīng)用還不能顯出弊端,但是大規(guī)模應(yīng)用出現(xiàn),領(lǐng)域?qū)ο筝^多時,必然出現(xiàn)怨言。而我 們使用AJAX的真實意圖并非來無趣地去解析各種各樣的XML,而是需要XML中代表的數(shù)據(jù)。至于XML是什么樣子,除了調(diào)試階段,沒有人會關(guān)心。
第二種XML的序列化方式是絕大多數(shù)跨進(jìn)程遠(yuǎn)程調(diào)用協(xié)議/框架所采取的方式。SOAP/WebService如此,XMLRPC如此,Buffalo采用 的Burlap也如此。這種遠(yuǎn)程調(diào)用的特征是,在協(xié)議約定的條件下,調(diào)用方和被調(diào)用方需要保證數(shù)據(jù)語義的完整性。拿什么保證?就是數(shù)據(jù)定義信息了。這些協(xié) 議的共同特征是采用謀一些標(biāo)記來描述數(shù)據(jù)類型:int, long, float, string, list...這樣定義完成后,只要根據(jù)協(xié)議,任何語言都能以一種通用的方式對數(shù)據(jù)進(jìn)行還原。AJAX引擎的概念也就漸漸呈現(xiàn)——通過某一種協(xié)議,他就處 于中間的位置,負(fù)責(zé)將調(diào)用方的請求包裝為協(xié)議,轉(zhuǎn)化為執(zhí)行方能夠理解的動作加以執(zhí)行;然后將執(zhí)行結(jié)果轉(zhuǎn)化為協(xié)議的值,傳遞給客戶端,客戶端引擎將協(xié)議內(nèi)容 解析為能夠理解的對象結(jié)構(gòu)傳遞給客戶端,然后就可以利用這個數(shù)據(jù)來顯示了。XML-RPC如此,WebService如此,DWR如此,JSON如此, Buffalo也如此。
綜上所述,純粹使用領(lǐng)域模型特定的輸出XML傳遞給客戶端是一種相當(dāng)原始的做法。他只在很低的層次上印證了所謂AJAX的概念。然而,這種概念的深入思維結(jié)果便是一個AJAX引擎。
文中提到的第二種輸出方式:HTML——應(yīng)該被看作WEB的一個特例,應(yīng)該說這是歷史因素、瀏覽器能力、設(shè)計者等多方因素達(dá)到的一個平衡。許多歷史應(yīng)用 中,大多采用將頁面進(jìn)行一定規(guī)則的分割,然后include或者jsp 2.0 tagfile的方式對公共區(qū)域進(jìn)行處理,留下一小部分進(jìn)行動態(tài)顯示。這里舉一個例子:查詢,顯示書籍列表。傳統(tǒng)做法就是上面一個搜索條件輸入文本框,下 面是搜索結(jié)果列表,處于同一個頁面。原來的搜索方式每次提交都要刷新整個頁面,用戶體驗不太好,現(xiàn)在需要改進(jìn)。按照激進(jìn)的Ajax做法應(yīng)該是當(dāng)搜索按鈕點 擊時,調(diào)用bookSearchService.search(String terms)的方法,取得結(jié)果列表,然后Ajax引擎處理結(jié)果數(shù)據(jù),將數(shù)據(jù)反序列化為javascript對象,開發(fā)者利用這些對象,要么利用DOM, 要么利用JavaScript Template, 在頁面對搜索結(jié)果進(jìn)行展示。然而,問題出現(xiàn)了:
搜索結(jié)果太多了,用DOM操作速度太慢;
開發(fā)者人手不夠,沒有時間,而這個頁面以前寫過;
那么出現(xiàn)這種情況時,很可能的做法便是,見原來那個搜索結(jié)果頁面刨去其他不相干的部分,留下搜索結(jié)果部分,然后利用一個resultDiv.innerHTML=xmlhttp.responseText完事。這樣做,既達(dá)到了改善體驗的效果,也提高了開發(fā)速度。
輸出HTML另外一種方式文中也沒有提及。事實上,HTML不僅僅作為片斷,更重要的是作為頁面視圖的一部分。在buffalo的demo中,可以看到所 有的頁面都被管理起來了,buffalo接管了視圖的切換;這種設(shè)計也存在于gmail/163新版郵箱中。這個應(yīng)用比上面的HTML片斷的使用方式還要 重要,因為這里是緩存可以介入的地方。通過不同的緩存策略,可以將用戶的實際和心理等待時間大大減少,從而進(jìn)一步改善用戶操作體驗,提升產(chǎn)品競爭力。 (PS. 在Buffalo 1.3中將加入客戶端緩存模塊,無需你動手,buffalo為你緩存視圖)
文中提及的第三種方式,JSON,根據(jù)第一部分的描述,應(yīng)該比較清楚了。實際上他扮演了一個Ajax引擎的角色。這里不得不提的是,使用JSON的相當(dāng)危 險的。因為他的協(xié)議表現(xiàn)與語言本身綁定太死。如果某一天,JSON協(xié)議變化了,那么使用JSON的應(yīng)用基本上不可能應(yīng)對這個變化——因為返回結(jié)果是 eval()得到的。而Buffalo將協(xié)議隱藏起來了,1.2版本甚至連服務(wù)器端的ServiceInvoker都將burlap實現(xiàn)依賴隔離開。現(xiàn)在 使用burlap,也許某一天不用了,buffalo的應(yīng)用照樣可以運行。因為里面的細(xì)節(jié)都是透明的,作為開發(fā)者,你只需要關(guān)注數(shù)據(jù)對象本身,而非用來描 述對象的那一堆字符。