国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
面向 Java 開發(fā)人員的 Ajax(二): Ajax 的 Java 對(duì)象序列化

面向 Java 開發(fā)人員的 Ajax: Ajax 的 Java 對(duì)象序列化

在 Ajax 應(yīng)用程序中序列化數(shù)據(jù)的五種途徑

級(jí)別: 中級(jí)

Philip McCarthy, 軟件開發(fā)顧問(wèn), 獨(dú)立顧問(wèn)

2005 年 10 月 24 日

如果您正在使用異步 JavaScript 和 XML(Ajax)進(jìn)行 Java™ Web 開發(fā),那么您最關(guān)心的問(wèn)題可能就是把數(shù)據(jù)從服務(wù)器傳遞給客戶機(jī)。在 面向 Java 開發(fā)人員的 Ajax 系列的第二篇文章中,Philip McCarthy 介紹了 Java 對(duì)象序列化的五種方式,并提供了選擇最適合應(yīng)用程序的數(shù)據(jù)格式和技術(shù)所需要的全部信息。

在這個(gè)系列的 第一篇文章 中,我介紹了 Ajax 的構(gòu)造塊:

  • 如何用 JavaScript XMLHttpRequest 對(duì)象從 Web 頁(yè)面向服務(wù)器發(fā)送異步請(qǐng)求。
  • 如何用 Java servlet 處理和響應(yīng)請(qǐng)求(向客戶機(jī)返回 XML 文檔)。
  • 如何在客戶端用響應(yīng)文檔更新頁(yè)面視圖。

這一次,我將繼續(xù)討論 Ajax 開發(fā)的基礎(chǔ)知識(shí),但是將側(cè)重于許多 Java Web 開發(fā)人員最關(guān)心的問(wèn)題:為客戶機(jī)生成數(shù)據(jù)。

多數(shù) Java 開發(fā)人員已經(jīng)把模型-視圖-控制器(MVC)模式應(yīng)用在他們的 Web 應(yīng)用程序上。在傳統(tǒng)的 Web 應(yīng)用程序中,視圖組件由 JSP 或者其他表示技術(shù)(例如 Velocity 模板)構(gòu)成。這些表示組件動(dòng)態(tài)地生成全新的 HTML 頁(yè)面,替代用戶以前正在查看的頁(yè)面,從而更新用戶界面。但是,在 Java Web 應(yīng)用程序使用 Ajax UI 的情況下,基于從 XMLHttpRequest 的響應(yīng)接收到的數(shù)據(jù),JavaScript 客戶端代碼對(duì)于更新用戶看到的內(nèi)容負(fù)有最終責(zé)任。從服務(wù)器的角度來(lái)看,視圖成為它響應(yīng)客戶機(jī)請(qǐng)求而發(fā)送的數(shù)據(jù)表示。

這篇文章側(cè)重于可以用來(lái)生成 Java 對(duì)象以數(shù)據(jù)為中心的視圖的技術(shù)。我將演示可以把 JavaBeans 變成 XML 文檔的各種方法,并且討論每種方法的優(yōu)劣。您將看到為什么 XML 并不總是最好的途徑:對(duì)于簡(jiǎn)單的 Ajax 請(qǐng)求來(lái)說(shuō),傳輸純文本更好。最后,我將介紹 JavaScript 對(duì)象標(biāo)注(JSON)。JSON 允許數(shù)據(jù)以序列化的 JavaScript 對(duì)象圖的形式傳輸,在客戶端代碼中處理序列化的 JavaScript 對(duì)象圖極為容易。

關(guān)于示例

我將使用一個(gè)示例應(yīng)用程序和幾個(gè)用例來(lái)演示這里討論的技術(shù)特性和技術(shù)。圖 1 顯示的極為簡(jiǎn)單的數(shù)據(jù)模型可以表示示例用例。這個(gè)模型代表在線商店中的顧客帳戶。顧客擁有以前訂單的集合,每個(gè)訂單包含幾個(gè)商品。


圖 1. 簡(jiǎn)單的對(duì)象模型

雖然 XMLHttpRequest 對(duì)于發(fā)送數(shù)據(jù)使用的格式?jīng)]有做任何限制,但是對(duì)于多數(shù)目的來(lái)說(shuō),只發(fā)送傳統(tǒng)的表單數(shù)據(jù)是適合的,所以我的討論集中在服務(wù)器的響應(yīng)上。響應(yīng)也可以有基于文本的格式,但是正如它的名字表示的,XMLHttpRequest 具有內(nèi)置的處理 XML 響應(yīng)數(shù)據(jù)的能力。這使 XML 成為 Ajax 響應(yīng)的默認(rèn)選擇,所以我們從 XML 格式開始討論。





回頁(yè)首


從 Java 類產(chǎn)生 XML

把 Ajax 響應(yīng)作為 XML 來(lái)傳遞有許多原因:每個(gè)支持 Ajax 的瀏覽器都有導(dǎo)航 XML 文檔的方法,也有許多服務(wù)器端技術(shù)可以處理 XML 數(shù)據(jù)。通過(guò)制定一個(gè)方案,描述要交換的文檔類型,在 Ajax 客戶端和服務(wù)器端之間很容易定義合約,而且如果服務(wù)器端架構(gòu)采用面向服務(wù)的方式,那么使用 XML 也可以允許非 Ajax 客戶機(jī)使用您提供的數(shù)據(jù)。

我將考慮從 Java 對(duì)象產(chǎn)生 XML 數(shù)據(jù)的三種方法,并討論每種方法的優(yōu)劣。





回頁(yè)首


自行進(jìn)行序列化

首先,可以從對(duì)象圖以編程的方式生成 XML。這種方式可以簡(jiǎn)單到只是在每個(gè) JavaBean 類中實(shí)現(xiàn) toXml() 方法即可。然后就可以選擇合適的 XML API,讓每個(gè) bean 提供表示自己狀態(tài)的元素,并遞歸地對(duì)自己的成員調(diào)用對(duì)象圖。顯然,這種方式無(wú)法擴(kuò)展到大量的類,因?yàn)槊總€(gè)類都需要專門編寫自己的 XML 生成代碼。從好的方面來(lái)看,這是一個(gè)實(shí)現(xiàn)起來(lái)簡(jiǎn)單的方式,沒有額外的配置支出或者更復(fù)雜的構(gòu)建過(guò)程支出,任何 JavaBean 圖都可以只用幾個(gè)調(diào)用就變成 XML 文檔。

在本系列 前一篇文章 的示例代碼中,我把 XML 標(biāo)記字符串連接在一起,實(shí)現(xiàn)了 toXml() 方法。上次我就提到過(guò),這是個(gè)糟糕的方法,因?yàn)樗汛_保標(biāo)記配對(duì)、實(shí)體編碼等工作的負(fù)擔(dān)放在每個(gè) toXml() 方法的代碼中。在 Java 平臺(tái)上有幾個(gè) XML API 可以替您做這些工作,這樣您就可以把精力集中在 XML 的內(nèi)容上。清單 1 用 JDOM API 實(shí)現(xiàn)了在線商店示例中表示訂單的類中的 toXml()(請(qǐng)參閱 圖 1)。


清單 1. Order 類的 toXml() 的 JDOM 實(shí)現(xiàn)
                        public Element toXml() {                        Element elOrder = new Element("order");                        elOrder.setAttribute("id",id);                        elOrder.setAttribute("cost",getFormattedCost());                        Element elDate = new Element("date").addContent(date);                        elOrder.addContent(elDate);                        Element elItems = new Element("items");                        for (Iterator<Item> iter =                        items.iterator() ; iter.hasNext() ; ) {                        elItems.addContent(iter.next().toXml());                        }                        elOrder.addContent(elItems);                        return elOrder;                        }                        

在這里可以看到用 JDOM 創(chuàng)建元素、使用屬性和添加元素內(nèi)容有多么簡(jiǎn)單。遞歸地調(diào)用復(fù)合 JavaBean 的 toXml() 方法是為了取得它們子圖的 Element 表示。例如,items 元素的內(nèi)容是通過(guò)調(diào)用 Order 聚合的每個(gè) Item 對(duì)象上的 toXml() 得到的。

一旦所有的 JavaBean 都實(shí)現(xiàn)了 toXml() 方法,那么把任意對(duì)象圖序列化成 XML 文檔并返回給 Ajax 客戶機(jī)就簡(jiǎn)單了,如清單 2 所示。


清單 2. 從 JDOM 元素生成 XML 響應(yīng)
                        public void doGet(HttpServletRequest req, HttpServletResponse res)                        throws java.io.IOException, ServletException {                        String custId = req.getParameter("username");                        Customer customer = getCustomer(custId);                        Element responseElem = customer.toXml();                        Document responseDoc = new Document(responseElem);                        res.setContentType("application/xml");                        new XMLOutputter().output(responseDoc,res.getWriter());                        }                        

JDOM 再次把工作變得非常簡(jiǎn)單。只需要在對(duì)象圖返回的 XML 元素外面包裝一個(gè) Document,然后用 XMLOutputter 把文檔寫入 servlet 響應(yīng)即可。清單 3 顯示了用這種方式生成的 XML 示例,用 JDOM Format.getPrettyFormat() 對(duì) XMLOutputter 進(jìn)行初始化,格式化得非常好。在這個(gè)示例中,顧客只做了一個(gè)訂單,包含兩個(gè)商品。


清單 3. 代表顧客的 XML 文檔
                        <?xml version="1.0" encoding="UTF-8"?>                        <customer username="jimmy66">                        <realname>James Hyrax</realname>                        <orders>                        <order id="o-11123" cost="$349.98">                        <date>08-26-2005</date>                        <items>                        <item id="i-55768">                        <name>Oolong 512MB CF Card</name>                        <description>512 Megabyte Type 1 CompactFlash card.                        Manufactured by Oolong Industries</description>                        <price>$49.99</price>                        </item>                        <item id="i-74491">                        <name>Fujak Superpix72 Camera</name>                        <description>7.2 Megapixel digital camera featuring six                        shooting modes and 3x optical zoom. Silver.</description>                        <price>$299.99</price>                        </item>                        </items>                        </order>                        </orders>                        </customer>                        

自行序列化的不足

有趣的是,清單 3 中的代碼展示了讓 JavaBean 把自己序列化為 XML 的一個(gè)主要不足。假設(shè)要用這個(gè)文檔表示顧客的訂單歷史視圖。在這種情況下,不太可能要顯示每個(gè)歷史訂單中每個(gè)商品的完整說(shuō)明,或者告訴顧客他或她自己的姓名。但是如果應(yīng)用程序有一個(gè) ProductSearch 類,它就是以 Item bean 列表的形式返回搜索結(jié)果,那么在 Item 的 XML 表示中包含說(shuō)明可能會(huì)有幫助。而且,Item 類上代表當(dāng)前庫(kù)存水平的額外字段,在產(chǎn)品搜索視圖中可能就是需要顯示的有用信息。但是,不管當(dāng)前的庫(kù)存水平是否與當(dāng)前情況相關(guān)(比如對(duì)顧客的訂單歷史來(lái)說(shuō)),這個(gè)字段都會(huì)從包含 Item 的任何對(duì)象圖中序列化出來(lái)。

從設(shè)計(jì)的角度來(lái)看,這是數(shù)據(jù)模型與視圖生成耦合的經(jīng)典問(wèn)題。每個(gè) bean 只能用一種途徑序列化自己,一成不變的方式意味著 Ajax 交互最終要交換它們不需要交換的數(shù)據(jù),因此造成客戶端代碼要從文檔中找到需要的信息更加困難,而且也會(huì)增加帶寬消耗和客戶端的 XML 解析時(shí)間。這種耦合的另一個(gè)后果就是 XML 的語(yǔ)法不能脫離 Java 類獨(dú)立變化。例如,對(duì)顧客文檔的方案做修改,可能會(huì)影響多個(gè) Java 類,造成它們也不得不做修改和重新編譯。

我稍后會(huì)解決這些問(wèn)題,但是首先來(lái)看一個(gè)對(duì)自行序列化方式的可伸縮性問(wèn)題的解決方案:XML 綁定框架。





回頁(yè)首


XML 綁定框架

近些年來(lái),已經(jīng)開發(fā)了多個(gè) Java API 來(lái)簡(jiǎn)化 XML 文檔到 Java 對(duì)象圖的綁定過(guò)程。多數(shù)都提供了 XML 編排和拆解;也就是說(shuō),它們可以在 Java 對(duì)象圖和 XML 之間執(zhí)行雙向會(huì)話。這些框架封裝了 XML 處理的全部工作,這意味著應(yīng)用程序代碼只需要處理普通的 Java 類。它們還希望提供有用的輔助功能,例如文檔驗(yàn)證。籠統(tǒng)來(lái)說(shuō),這些框架采用了兩種不同的方式:代碼生成和對(duì)象到 XML 映射。我將分別解釋這兩種方式。

代碼生成方式

使用代碼生成的框架包括 XMLBeans、JAXB、Zeus 和 JBind。Castor 也能使用這項(xiàng)技術(shù)。這類框架的起點(diǎn)是描述文檔數(shù)據(jù)類型的 XML 方案。使用框架提供的工具,就可以生成代表這些方案定義類型的 Java 類。最后,用這些生成的類編寫應(yīng)用程序,表示自己的模型數(shù)據(jù),并通過(guò)框架提供的一些輔助機(jī)制把數(shù)據(jù)序列化成 XML。

如果應(yīng)用程序要使用大型 XML 語(yǔ)法,那么代碼生成方式是個(gè)很好的方法。在數(shù)十個(gè)類上編寫定制 XML 序列化代碼的可伸縮性問(wèn)題由此消除。另一方面,也不再需要定義自己的 JavaBean??蚣苌傻?Java 類通常非常符合 XML 的結(jié)構(gòu),所以對(duì)它們進(jìn)行編碼很難。而且,生成的類變成啞數(shù)據(jù)容器,因?yàn)橐话悴荒芟蛩鼈兲砑有袨?。一般?lái)說(shuō),在應(yīng)用程序代碼中要做些妥協(xié),才能很好地處理方案生成的類型。另一個(gè)缺陷是如果修改方案,會(huì)造成生成的類也要修改,所以也就會(huì)對(duì)圍繞它們編寫的代碼帶來(lái)相應(yīng)的影響。

這種類型的 XML 綁定框架在數(shù)據(jù)拆解時(shí)最有用(例如,使用 XML 文檔并把它們轉(zhuǎn)化成 Java 對(duì)象)。除非擁有大型數(shù)據(jù)模型而且有可能從生成的類中獲益,否則基于代碼生成的框架對(duì)于 Ajax 應(yīng)用程序來(lái)說(shuō)可能有很大的殺傷力。

映射方式

采用映射方式的框架包括 Castor 和 Apache Commons Betwixt。映射通常是比代碼生成更靈活和更輕量的解決方案。首先,可以像通常一樣編寫 JavaBean,包括任何行為以及任何自己喜歡的方便的方法。然后,在運(yùn)行時(shí),調(diào)用框架中基于內(nèi)省的編排器,并根據(jù)對(duì)象成員的類型、名稱和值生成 XML 文檔。通過(guò)定義類的映射文件,可以覆蓋默認(rèn)的綁定策略,并就類在 XML 中的表示方式對(duì)編排器提出建議。

這種方法是在可伸縮性與靈活性之間的良好折中??梢园凑兆约合矚g的方式編寫 Java 類,編排器負(fù)責(zé)處理 XML。雖然映射定義文件編寫起來(lái)簡(jiǎn)單,可伸縮性也足夠好,但是映射規(guī)則最多只能改變標(biāo)準(zhǔn)的綁定行為,而且在對(duì)象結(jié)構(gòu)和它們的 XML 表示之間總要?dú)埩粢恍詈?。最終,可能不得不在 Java 表示或 XML 格式之間任選一個(gè)做些折中,才能讓映射方法起作用。

數(shù)據(jù)綁定總結(jié)

Dennis Sosnoski 就 XML 數(shù)據(jù)綁定 API 的主題,在代碼生成和代碼映射兩個(gè)方面寫了深入的文章。如果想進(jìn)一步研究這個(gè)領(lǐng)域,我推薦他在 Castor 和代碼生成框架方面的精彩文章(請(qǐng)參閱 參考資料 中的鏈接)。

總之,代碼生成方式損失了過(guò)多的靈活性和方便性,對(duì)于典型的 Ajax 應(yīng)用程序用處不大。另一方面,基于映射的框架可能工作得很好,但是要恰到好處地調(diào)整它們的映射策略,以便從對(duì)象生成需要的 XML。

所有的 XML 綁定 API 都具有手工序列化技術(shù)的一個(gè)主要不足:模型和視圖的耦合。被限制為一個(gè)類型一個(gè) XML 表示,就意味著在網(wǎng)絡(luò)上總要有冗余數(shù)據(jù)傳輸。更嚴(yán)重的問(wèn)題是,在情況要求客戶端代碼使用專門視圖時(shí),客戶端代碼卻無(wú)法得到它,所以可能要費(fèi)力地處理給定對(duì)象圖的一成不變的視圖。

在傳統(tǒng)的 Web 應(yīng)用程序開發(fā)中,采用頁(yè)面模板系統(tǒng)把視圖生成與控制器邏輯和模型數(shù)據(jù)干凈地分離。這種方法在 Ajax 場(chǎng)景中也會(huì)有幫助。





回頁(yè)首


頁(yè)面模板系統(tǒng)

任何通用目的的頁(yè)面模板技術(shù)都可以用來(lái)生成 XML,從而使 Ajax 應(yīng)用程序根據(jù)自己的數(shù)據(jù)模型生成任何 XML 響應(yīng)文檔。額外收獲是:模板可以用簡(jiǎn)單的、表現(xiàn)力強(qiáng)的標(biāo)記語(yǔ)言編寫,而不是用一行行的 Java 代碼編寫。清單 5 是一個(gè) JSP 頁(yè)面,采用了 Customer bean 并表示出定制的 XML 視圖,適合客戶端代碼生成訂單歷史組件。


清單 4. 生成訂單歷史文檔的 JSP
                        <?xml version="1.0"?>                        <%@ page contentType="application/xml" %>                        <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>                        <c:set var="cust" value="${requestScope.customer}"/>                        <orderhistory username="${cust.username}">                        <c:forEach var="order" items="${cust.orders}">                        <order id="${order.id}" cost="${order.formattedCost}">                        <date>${order.date}</date>                        <items>                        <c:forEach var="item" items="${order.items}">                        <item id="${item.id}">                        <name><c:out value="${item.name}" escapeXml="true"/></name>                        <price>${item.formattedPrice}</price>                        </item>                        </c:forEach>                        </items>                        </order>                        </c:forEach>                        </orderhistory>                        

這個(gè)簡(jiǎn)潔的模板只輸出訂單歷史視圖需要的數(shù)據(jù),不輸出不相關(guān)的資料(例如商品說(shuō)明)。創(chuàng)建產(chǎn)品搜索視圖的定制 XML 應(yīng)當(dāng)同樣簡(jiǎn)單,這個(gè)視圖包含每個(gè)商品的完整說(shuō)明和庫(kù)存水平。

模板的問(wèn)題

另一方面,現(xiàn)在我需要為每個(gè)不同視圖創(chuàng)建一個(gè)新 JSP,而不能僅僅把需要的對(duì)象圖組織起來(lái)并序列化它。從設(shè)計(jì)的角度來(lái)說(shuō),許多人可能會(huì)有爭(zhēng)議,認(rèn)為這無(wú)論如何是件好事,因?yàn)檫@意味著正式地考慮服務(wù)器要生成的文檔類型。而且,因?yàn)槲椰F(xiàn)在要處理通用的模板環(huán)境,而不是特定于 XML 的 API,所以確保標(biāo)記匹配、元素和屬性的順序正確以及 XML 實(shí)體(例如 < 或 &)正確轉(zhuǎn)義就成了我的責(zé)任。JSP 的核心 out 標(biāo)記使后面這項(xiàng)工作變得很容易,但是不是所有的模板技術(shù)都提供了這樣的機(jī)制。最后,沒有方便的途徑可以在服務(wù)器端根據(jù)方案檢驗(yàn)生成的 XML 文檔的正確性,但這畢竟不是要在生產(chǎn)環(huán)境中做的事,可以方便地在開發(fā)期間處理它。





回頁(yè)首


不用 XML 的響應(yīng)數(shù)據(jù)

迄今為止,我介紹的所有技術(shù)都用 XML 文檔的形式生成服務(wù)器響應(yīng)。但是,XML 有一些問(wèn)題。其中一個(gè)就是延遲。瀏覽器不能立即解析 XML 文檔并生成 DOM 模型,所以這會(huì)降低某些 Ajax 組件需要的“迅捷”感,特別是在較慢的機(jī)器上解析大型文檔的時(shí)候更是如此。“現(xiàn)場(chǎng)搜索”就是一個(gè)示例,在這種搜索中,當(dāng)用戶輸入搜索術(shù)語(yǔ)時(shí),就會(huì)從服務(wù)器提取搜索結(jié)果并顯示給用戶。對(duì)于現(xiàn)場(chǎng)搜索組件來(lái)說(shuō),迅速地響應(yīng)輸入是非常重要的,但是同時(shí)它還需要迅速而持續(xù)地解析服務(wù)器的響應(yīng)。

延遲是一個(gè)重要的考慮因素,但是避免使用 XML 的最大原因是差勁的客戶端 DOM API。清單 5 顯示了使用跨瀏覽器兼容的方式通過(guò) DOM 得到某個(gè)值的時(shí)候,通常不得不面對(duì)的困難。


清單 5. 在 JavaScript 中導(dǎo)航 XML 響應(yīng)文檔
                        // Find name of first item in customer‘s last order                        var orderHistoryDoc = req.responseXML;                        var orders = orderHistoryDoc.getElementsByTagName("order");                        var lastOrder = orders[orders.length - 1];                        var firstItem = lastOrder.getElementsByTagName("item")[0];                        var itemNameElement = firstItem.firstChild;                        var itemNameText = itemNameElement.firstChild.data;                        

當(dāng)元素中間存在空白時(shí),情況就變得更加復(fù)雜,因?yàn)槊總€(gè)元素的 firstChild 經(jīng)常是個(gè)空白文本節(jié)點(diǎn)?,F(xiàn)在有 JavaScript 庫(kù)可以緩解處理 XML 文檔的麻煩。這些庫(kù)包括 Sarissa (請(qǐng)參閱 參考資料)和 Google-ajaXSLT,這兩個(gè)庫(kù)都把 XPath 功能添加到了大多數(shù)瀏覽器中。

但是,想想替代方案還是值得的。除了 responseXML 之外,XMLHttpRequest 對(duì)象還提供了名為 responseText 的屬性,這個(gè)屬性只是以字符串的方式提供服務(wù)器的響應(yīng)體。

responseText 屬性

當(dāng)服務(wù)器需要向客戶機(jī)發(fā)送非常簡(jiǎn)單的值時(shí),responseText 特別方便,它可以避免 XML 導(dǎo)致的帶寬支出和處理支出。例如,簡(jiǎn)單的 true/false 響應(yīng)可以由服務(wù)器以純文本方式返回,可以是逗號(hào)分隔的簡(jiǎn)單的名稱或數(shù)字列表。但是,一般來(lái)說(shuō),最好不要在同一個(gè)應(yīng)用程序中把 XML 響應(yīng)和純文本響應(yīng)混合使用;保持單一數(shù)據(jù)格式可以讓代碼抽象和重用更加簡(jiǎn)單。

responseText 與 XML 響應(yīng)數(shù)據(jù)結(jié)合時(shí)也會(huì)有用。在只需要從響應(yīng)文檔中提取單一值的場(chǎng)景中,“欺騙性”地把 XML 當(dāng)作文本字符串,而不把它當(dāng)作結(jié)構(gòu)化的文檔對(duì)待,會(huì)更方便。例如,清單 6 顯示了如何用正則表達(dá)式從顧客的訂單歷史中提取第一筆訂單的日期。不過(guò),這實(shí)際是種花招,一般不應(yīng)當(dāng)依賴 XML 文檔的詞匯表達(dá)。


清單 6. 用正則表達(dá)式處理 XMLHttpRequest 的 responseText 對(duì)象
                        var orderHistoryText = req.responseText;                        var matches = orderHistoryText.match(/<date>(.*?)<\/date>/);                        var date = matches[1];                        

在某些情況下,采用即時(shí)方式使用 responseText 會(huì)比較方便。但是,理想情況下,應(yīng)當(dāng)有種途徑,可以用一種能夠讓 JavaScript 輕松導(dǎo)航、卻沒有 XML 處理支出的格式表示復(fù)雜的結(jié)構(gòu)化數(shù)據(jù)。幸運(yùn)的是,確實(shí)存在這樣一種格式。





回頁(yè)首


JavaScript 對(duì)象標(biāo)注

實(shí)際上,JavaScript 對(duì)象的大部分都由聯(lián)合數(shù)組、數(shù)字索引數(shù)組、字符串、數(shù)字或者這些類型的嵌套組合而成。因?yàn)樗蓄愋投伎梢杂?JavaScript 直接聲明,所以可以在一條語(yǔ)句中靜態(tài)地定義對(duì)象圖。清單 7 使用 JSON 語(yǔ)法聲明了一個(gè)對(duì)象,并演示了如何訪問(wèn)這個(gè)對(duì)象。大括號(hào)表示聯(lián)合數(shù)組(即對(duì)象),它的鍵 -值組合由逗號(hào)分隔。方括號(hào)表示數(shù)字索引數(shù)組。


清單 7. 用 JSON 在 JavaScript 中直接聲明一個(gè)簡(jiǎn)單對(duì)象
                        var band = {                        name: "The Beatles",                        members: [                        {                        name: "John",                        instruments: ["Vocals","Guitar","Piano"]                        },                        {                        name: "Paul",                        instruments: ["Vocals","Bass","Piano","Guitar"]                        },                        {                        name: "George",                        instruments: ["Guitar","Vocals"]                        },                        {                        name: "Ringo",                        instruments: ["Drums","Vocals"]                        }                        ]                        };                        // Interrogate the band object                        var musician = band.members[3];                        alert( musician.name                        + " played " + musician.instruments[0]                        + " with " + band.name );                        

既然 JSON 是一個(gè)有趣的語(yǔ)言特性,那么它對(duì) Ajax 有什么意義呢?妙處在于可以用 JSON 在 Ajax 服務(wù)器響應(yīng)中通過(guò)網(wǎng)絡(luò)發(fā)送 JavaScript 對(duì)象圖。這意味著在客戶端可以避免使用笨拙的 DOM API 對(duì) XML 進(jìn)行導(dǎo)航 —— 只需要分析 JSON 響應(yīng),就會(huì)立即得到可以訪問(wèn)的 JavaScript 對(duì)象圖。但是,首先需要把 JavaBean 變成 JSON。

從 Java 類產(chǎn)生 JSON

不同 XML 生成技術(shù)所具有的優(yōu)缺點(diǎn)也適用于 JSON 的產(chǎn)生。而且可以證明,存在需要再次使用表示模板技術(shù)的情況。但是,使用 JSON 在理念上更接近于在應(yīng)用層之間傳遞序列化的對(duì)象,而不是創(chuàng)建應(yīng)用程序狀態(tài)的視圖。我將介紹如何用 org.json 這個(gè) Java API 在 Java 類上創(chuàng)建 toJSONObject() 方法。然后,就可以把 JSONObject 簡(jiǎn)單地序列化成 JSON。清單 8 反映了 清單 1 討論的 XML,顯示了 Order 類的 toJSONObject() 實(shí)現(xiàn)。


清單 8. Order 類的 toJSONObject() 方法實(shí)現(xiàn)
                        public JSONObject toJSONObject() {                        JSONObject json = new JSONObject();                        json.put("id",id);                        json.put("cost",getFormattedCost());                        json.put("date",date);                        JSONArray jsonItems = new JSONArray();                        for (Iterator<Item> iter =                        items.iterator() ; iter.hasNext() ; ) {                        jsonItems.put(iter.next().toJSONObject());                        }                        json.put("items",jsonItems);                        return json;                        }                        

可以看到,org.json API 非常簡(jiǎn)單。 JSONObject 代表 JavaScript 對(duì)象(即聯(lián)合數(shù)組),有不同的 put() 方法,方法接受的 String 鍵和值是原生類型、String 類型或其他 JSON 類型。JSONArray 代表索引數(shù)組,所以它的 put() 方法只接受一個(gè)值。請(qǐng)注意在清單 8 中,創(chuàng)建 jsonItems 數(shù)組,然后再用 put() 把它附加到 json 對(duì)象上;可以用另外一種方法做這項(xiàng)工作,就是對(duì)每個(gè)項(xiàng)目調(diào)用 json.accumulate("items",iter.next().toJSONObject());。accumulate() 方法與 put() 類似,區(qū)別在于它把值添加到按照鍵進(jìn)行識(shí)別的索引數(shù)組。

清單 9 顯示了如何序列化 JSONObject 并把它寫入 servlet 響應(yīng)。


清單 9. 從 JSONObject 生成序列化的 JSON 響應(yīng)
                        public void doGet(HttpServletRequest req, HttpServletResponse res)                        throws java.io.IOException, ServletException {                        String custId = req.getParameter("username");                        Customer customer = getCustomer(custId);                        res.setContentType("application/x-json");                        res.getWriter().print(customer.toJSONObject());                        }                        

可以看到,它實(shí)際上什么也沒有做。在這里隱式調(diào)用的 JSONObject 的 toString() 方法做了所有工作。請(qǐng)注意,application/x-json 內(nèi)容類型還有一點(diǎn)不確定 —— 在編寫這篇文章的時(shí)候,關(guān)于 JSON 應(yīng)當(dāng)屬于什么 MIME 類型還沒有定論。但是,目前 application/x-json 是合理的選擇。清單 10 顯示了這個(gè) servlet 代碼的示例響應(yīng)。


清單 10. Customer bean 的 JSON 表示
                        {                        "orders": [                        {                        "items": [                        {                        "price": "$49.99",                        "description": "512 Megabyte Type 1 CompactFlash card.                        Manufactured by Oolong Industries",                        "name": "Oolong 512MB CF Card",                        "id": "i-55768"                        },                        {                        "price": "$299.99",                        "description": "7.2 Megapixel digital camera featuring six                        shooting modes and 3x optical zoom. Silver.",                        "name": "Fujak Superpix72 Camera",                        "id": "i-74491"                        }                        ],                        "date": "08-26-2005",                        "cost": "$349.98",                        "id": "o-11123"                        }                        ],                        "realname": "James Hyrax",                        "username": "jimmy66"                        }                        

在客戶端使用 JSON

處理的最后一步是把在客戶端把 JSON 數(shù)據(jù)變成 JavaScript 對(duì)象。這可以通過(guò)對(duì) eval() 的簡(jiǎn)單調(diào)用實(shí)現(xiàn),這個(gè)函數(shù)可以即時(shí)地解釋包含 JavaScript 表達(dá)式的字符串。清單 11 把 JSON 響應(yīng)轉(zhuǎn)變成 JavaScript 對(duì)象圖,然后執(zhí)行清單 5 的任務(wù),從顧客的最后一次訂單中得到第一個(gè)商品的名稱。


清單 11. 評(píng)估 JSON 響應(yīng)
                        var jsonExpression = "(" + req.responseText + ")";                        var customer = eval(jsonExpression);                        // Find name of first item in customer‘s last order                        var lastOrder = customer.orders[customer.orders.length-1];                        var name = lastOrder.items[0].name;                        

比較清單 11 和 清單 5 可以發(fā)現(xiàn)使用 JSON 的客戶端的優(yōu)勢(shì)。如果在 Ajax 項(xiàng)目中要在客戶端對(duì)許多復(fù)雜的服務(wù)器響應(yīng)進(jìn)行導(dǎo)航,那么 JSON 可能適合您的需要。JSON 和 XMLHttpRequest 結(jié)合還會(huì)讓 Ajax 交互看起來(lái)更像 RPC 調(diào)用而不是 SOA 請(qǐng)求,這對(duì)應(yīng)用程序的設(shè)計(jì)可能會(huì)有意義。在下一篇文章中,我要研究的框架,就是明確地為了讓 JavaScript 代碼對(duì)服務(wù)器端對(duì)象進(jìn)行遠(yuǎn)程方法調(diào)用而設(shè)計(jì)的。

JSON 的不足

JSON 也有它的不足。使用這里介紹的 JSON 方式,就沒有辦法針對(duì)每個(gè)請(qǐng)求對(duì)對(duì)象的序列化進(jìn)行裁剪,所以不需要的字段可能經(jīng)常會(huì)在網(wǎng)絡(luò)上發(fā)送。另外,添加 toJSONObject() 方法到每個(gè) JavaBean,可伸縮性不太好,雖然用內(nèi)省和標(biāo)注編寫一個(gè)通用的 JavaBean 到 JSON 的序列化器可能很簡(jiǎn)單。最后,如果服務(wù)器端代碼是面向服務(wù)的,沒有單獨(dú)針對(duì)處理 Ajax 客戶請(qǐng)求調(diào)整過(guò),那么由于對(duì) XML 一致的支持,XML 會(huì)是更好的選擇。





回頁(yè)首


比較序列化技術(shù)

現(xiàn)在已經(jīng)看到了把 Java 狀態(tài)傳輸?shù)?Ajax 客戶端的五種不同技術(shù)。我討論了自行手工編碼 XML 序列化、通過(guò)代碼生成的 XML 綁定、通過(guò)映射機(jī)制的 XML 綁定、基于模板的 XML 生成以及手工編碼到 JSON 的序列化。每種技術(shù)都有自己的優(yōu)勢(shì)和不足,分別適用于不同的應(yīng)用程序架構(gòu)。

為了總結(jié)每種方式的優(yōu)勢(shì)與不足,表 1 從六個(gè)方面進(jìn)行了粗略的評(píng)分:

可伸縮性
描述技術(shù)適應(yīng)大量數(shù)據(jù)類型的容易程度。對(duì)于每個(gè)附加類型,編碼和配置工作量是否會(huì)增長(zhǎng)?
易于集成
評(píng)估把技術(shù)集成到項(xiàng)目的簡(jiǎn)單程度。是否需要更加復(fù)雜的構(gòu)建過(guò)程?是否增加了部署的復(fù)雜性?
Java 類 API
描述以指定方式處理服務(wù)器端 Java 對(duì)象的容易程度。是可以編寫普通的 bean,還是不得不處理笨拙的文檔表示?
對(duì)輸出的控制
描述對(duì)類的序列化表示控制的精確程度。
視圖靈活性
評(píng)估從同一組對(duì)象是否可以創(chuàng)建不同的、定制的數(shù)據(jù)序列化。
客戶端數(shù)據(jù)訪問(wèn)
描述 JavaScript 代碼處理服務(wù)器響應(yīng)數(shù)據(jù)的難易程度。

表 1. 數(shù)據(jù)生成技術(shù)的相對(duì)價(jià)值
自行編寫 XML 通過(guò)代碼生成的 XML 綁定 通過(guò)映射的 XML 綁定 頁(yè)面模板 XML 手工編碼的 JSON 序列化
可伸縮性 一般 一般
易于集成 一般 一般
Java 類 API
對(duì)輸出的控制 一般
視圖靈活性
客戶端數(shù)據(jù)訪問(wèn) 一般





回頁(yè)首


結(jié)束語(yǔ)

表 1 中的數(shù)據(jù)并不表明某項(xiàng)序列化技術(shù)比其他的技術(shù)好。畢竟,六種標(biāo)準(zhǔn)的相對(duì)重要性取決于項(xiàng)目的具體情況。例如,如果要處理數(shù)百種數(shù)據(jù)類型,這時(shí)想要的是可伸縮性,那么代碼生成可能就是最好的選擇。如果需要為同一數(shù)據(jù)模型生成多個(gè)不同視圖,那么就應(yīng)當(dāng)使用頁(yè)面模板。如果處理的是小規(guī)模項(xiàng)目,想降低需要編寫的 JavaScript 代碼數(shù)量,那么請(qǐng)考慮 JSON。

希望這篇文章為您提供了選擇適合自己應(yīng)用程序的序列化技術(shù)所需要的信息。請(qǐng)參閱 參考資料 一節(jié),學(xué)習(xí)關(guān)于這里討論的技術(shù)的更多內(nèi)容。您還應(yīng)當(dāng)繼續(xù)關(guān)注這個(gè)系列的下一篇文章,在下一篇文章中,我將介紹如何用直接 Web 遠(yuǎn)程(DWR)編寫 Java Ajax 應(yīng)用程序。DWR 框架支持從 JavaScript 代碼中直接調(diào)用 Java 類上的方法。換句話說(shuō),它替您負(fù)責(zé)數(shù)據(jù)序列化的工作,所以您可以在更高的抽象層次上使用 Ajax。





回頁(yè)首


參考資料

學(xué)習(xí)

獲得產(chǎn)品和技術(shù)
  • Sarissa:一個(gè)支持 XPath 的跨瀏覽器的 JavaScript 庫(kù)。

  • Jakarta Commons Betwixt:一個(gè)靈活的基于映射的 XML 綁定庫(kù)。

  • JSON:用于 Java 和其他語(yǔ)言的 JavaScript 對(duì)象標(biāo)注 API。


討論




回頁(yè)首


關(guān)于作者

Philip McCarthy 是一位軟件開發(fā)顧問(wèn),專攻 Java 和 Web 技術(shù)。他目前在位于 Bristol 的 HP 實(shí)驗(yàn)室從事 Hewlett Packard 數(shù)字媒體平臺(tái)的工作 。在最近幾年中,Phil 開發(fā)了多個(gè)采用異步服務(wù)器通信和 DOM 腳本的富 Web 客戶端。他很高興我們現(xiàn)在有了一個(gè)針對(duì)它們的名稱??梢酝ㄟ^(guò) Phil 的電子郵件 philmccarthy@gmail.com 與他聯(lián)系。

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Web2.0十大Ajax安全漏洞
Ajax支持的Google地圖Mashup教程(2)
什么是Ajax和JSON,它們的優(yōu)缺點(diǎn)
WCF與AJAX編程開發(fā)實(shí)踐(4):支持JSON的AJAX WCF服務(wù)
MVC中Return Json()
JS中的JSON對(duì)象
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服