久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

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

 昵稱2807 2006-10-16

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

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

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

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

2005 年 10 月 24 日

如果您正在使用異步 JavaScript 和 XML(Ajax)進(jìn)行 Java™ Web 開(kāi)發(fā),,那么您最關(guān)心的問(wèn)題可能就是把數(shù)據(jù)從服務(wù)器傳遞給客戶機(jī),。在 面向 Java 開(kāi)發(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 開(kāi)發(fā)的基礎(chǔ)知識(shí),,但是將側(cè)重于許多 Java Web 開(kāi)發(fā)人員最關(guān)心的問(wèn)題:為客戶機(jī)生成數(shù)據(jù)。

多數(shù) Java 開(kāi)發(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ì)象模型
代表顧客帳戶的對(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 格式開(kāi)始討論,。





回頁(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è)類都需要專門(mén)編寫(xiě)自己的 XML 生成代碼,。從好的方面來(lái)看,這是一個(gè)實(shí)現(xiàn)起來(lái)簡(jiǎn)單的方式,,沒(méi)有額外的配置支出或者更復(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 把文檔寫(xiě)入 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)開(kāi)發(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 類,。最后,用這些生成的類編寫(xiě)應(yīng)用程序,,表示自己的模型數(shù)據(jù),,并通過(guò)框架提供的一些輔助機(jī)制把數(shù)據(jù)序列化成 XML。

如果應(yīng)用程序要使用大型 XML 語(yǔ)法,,那么代碼生成方式是個(gè)很好的方法,。在數(shù)十個(gè)類上編寫(xiě)定制 XML 序列化代碼的可伸縮性問(wèn)題由此消除。另一方面,,也不再需要定義自己的 JavaBean,。框架生成的 Java 類通常非常符合 XML 的結(jié)構(gòu),,所以對(duì)它們進(jìn)行編碼很難,。而且,,生成的類變成啞數(shù)據(jù)容器,,因?yàn)橐话悴荒芟蛩鼈兲砑有袨椤R话銇?lái)說(shuō),,在應(yīng)用程序代碼中要做些妥協(xié),,才能很好地處理方案生成的類型。另一個(gè)缺陷是如果修改方案,,會(huì)造成生成的類也要修改,,所以也就會(huì)對(duì)圍繞它們編寫(xiě)的代碼帶來(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,。映射通常是比代碼生成更靈活和更輕量的解決方案。首先,,可以像通常一樣編寫(xiě) JavaBean,,包括任何行為以及任何自己喜歡的方便的方法。然后,,在運(yùn)行時(shí),,調(diào)用框架中基于內(nèi)省的編排器,并根據(jù)對(duì)象成員的類型,、名稱和值生成 XML 文檔,。通過(guò)定義類的映射文件,,可以覆蓋默認(rèn)的綁定策略,并就類在 XML 中的表示方式對(duì)編排器提出建議,。

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

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

Dennis Sosnoski 就 XML 數(shù)據(jù)綁定 API 的主題,,在代碼生成和代碼映射兩個(gè)方面寫(xiě)了深入的文章,。如果想進(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)題是,在情況要求客戶端代碼使用專門(mén)視圖時(shí),,客戶端代碼卻無(wú)法得到它,,所以可能要費(fèi)力地處理給定對(duì)象圖的一成不變的視圖。

在傳統(tǒng)的 Web 應(yīng)用程序開(kāi)發(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ǔ)言編寫(xiě),,而不是用一行行的 Java 代碼編寫(xiě),。清單 5 是一個(gè) JSP 頁(yè)面,,采用了 Customer bean 并表示出定制的 XML 視圖,適合客戶端代碼生成訂單歷史組件,。


清單 4. 生成訂單歷史文檔的 JSP
                        <?xml version="1.0"?>
                        <%@ page contentType="application/xml" %>
                        <%@ taglib uri="http://java./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ī)制,。最后,,沒(méi)有方便的途徑可以在服務(wù)器端根據(jù)方案檢驗(yàn)生成的 XML 文檔的正確性,,但這畢竟不是要在生產(chǎn)環(huán)境中做的事,,可以方便地在開(kāi)發(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)。現(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)航、卻沒(méi)有 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 并把它寫(xiě)入 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í)際上什么也沒(méi)有做。在這里隱式調(diào)用的 JSONObject 的 toString() 方法做了所有工作,。請(qǐng)注意,,application/x-json 內(nèi)容類型還有一點(diǎn)不確定 —— 在編寫(xiě)這篇文章的時(shí)候,關(guān)于 JSON 應(yīng)當(dāng)屬于什么 MIME 類型還沒(méi)有定論,。但是,,目前 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 方式,,就沒(méi)有辦法針對(duì)每個(gè)請(qǐng)求對(duì)對(duì)象的序列化進(jìn)行裁剪,所以不需要的字段可能經(jīng)常會(huì)在網(wǎng)絡(luò)上發(fā)送。另外,,添加 toJSONObject() 方法到每個(gè) JavaBean,,可伸縮性不太好,雖然用內(nèi)省和標(biāo)注編寫(xiě)一個(gè)通用的 JavaBean 到 JSON 的序列化器可能很簡(jiǎn)單,。最后,,如果服務(wù)器端代碼是面向服務(wù)的,沒(méi)有單獨(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ì)象的容易程度。是可以編寫(xiě)普通的 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à)值
自行編寫(xiě) 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)目,,想降低需要編寫(xiě)的 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)編寫(xiě) 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 是一位軟件開(kāi)發(fā)顧問(wèn),,專攻 Java 和 Web 技術(shù),。他目前在位于 Bristol 的 HP 實(shí)驗(yàn)室從事 Hewlett Packard 數(shù)字媒體平臺(tái)的工作 。在最近幾年中,,Phil 開(kāi)發(fā)了多個(gè)采用異步服務(wù)器通信和 DOM 腳本的富 Web 客戶端,。他很高興我們現(xiàn)在有了一個(gè)針對(duì)它們的名稱??梢酝ㄟ^(guò) Phil 的電子郵件 [email protected] 與他聯(lián)系,。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多