隨筆 - 45, 文章 - 6, 評論 - 4, 引用 - 0
          數(shù)據(jù)加載中……

          在Java中使用DOM和XPath進行有效的XML處理

          文檔對象模型(Document Object Model,DOM)是公認(rèn)的 W3C 標(biāo)準(zhǔn),它被用于與平臺及語言無關(guān)的 XML
          文檔內(nèi)容、結(jié)構(gòu)和樣式的動態(tài)訪問和更新。它為表示文檔定義了一套標(biāo)準(zhǔn)的接口集,也為訪問和操縱文檔定義了一套標(biāo)準(zhǔn)的方法。DOM
          得到廣泛的支持和普及,并且它以各種不同的語言實現(xiàn),包括 Java、Perl、C、C++、VB、Tcl 和 Python.
            正如我將在本文所演示的,當(dāng)基于流的模型(例如 SAX)不能滿足 XML 處理要求時,DOM
          是一個極佳的選擇。不幸的是,規(guī)范的幾個方面,例如其語言無關(guān)性接口和“一切都是節(jié)點(everything-is-a-node)”抽象概念的使用,使
          其難以使用且易于生成脆弱代碼。這在最近的幾個大型 DOM
          項目的研究中尤其明顯,這些項目是由許多開發(fā)人員過去一年所創(chuàng)建的。下面討論了常見的問題及其補救措施。
            文檔對象模型
            DOM 規(guī)范被設(shè)計成可與任何編程語言一起使用。因此,它嘗試使用在所有語言中都可用的一組通用的、核心的功能部件。DOM
          規(guī)范同樣嘗試保持其接口定義方面的無關(guān)性。這就允許 Java 程序員在使用 Visual Basic 或 Perl 時應(yīng)用他們的 DOM
          知識,反之亦然。
            該規(guī)范同樣將文檔的每個部分看成由類型和值組成的節(jié)點。這為處理文檔的所有方面提供了完美的概念性框架。例如,下面的 XML 片段
            the Italicized portion.
            就是通過以下的 DOM 結(jié)構(gòu)表示的:
            圖 1:XML 文檔的 DOM 表示
            樹的每個Document 、 Element 、 Text 和 Attr 部分都是 DOM Node 。
            完美的抽象確實付出了代價??紤] XML 片段: Value 。您或許會認(rèn)為文本的值可以通過普通的 Java String
          對象來表示,并且通過簡單的 getValue 調(diào)用可訪問。實際上,文本被當(dāng)成 tagname 節(jié)點下的一個或多個子 Node
          。因此,為了獲取文本值,您需要遍歷 tagname 的子節(jié)點,將每個值整理成一個字符串。這樣做有充分的理由: tagname
          可能包含其它嵌入的 XML 元素,在這種情況下獲取其文本值沒有多大意義。然而,在現(xiàn)實世界中,我們看到由于缺乏便利的函數(shù)導(dǎo)致頻繁的編碼錯誤占了
          80% 的情況,這樣做的確有意義。
            設(shè)計問題
            DOM 語言無關(guān)性的缺點是通常在每個編程語言中使用的一整套工作方法和模式不能被使用。例如,不能使用熟悉的 Java new
          構(gòu)造創(chuàng)建新的 Element ,開發(fā)者必須使用工廠構(gòu)造器方法。 Node 的集合被表示成 NodeList ,而不是通常的 List 或
          Iterator 對象。這些微小的不便意味著不同尋常的編碼實踐和增多的代碼行,并且它們迫使程序員學(xué)習(xí) DOM 的行事方法而不是用直覺的方法。
            DOM 使用“一切都是節(jié)點”的抽象。這就意味著幾乎 XML 文檔的每個部分,例如: Document 、 Element 和
          Attr ,全都繼承( extend ) Node 接口。這不僅是概念上完美,而且還允許每個 DOM
          的不同實現(xiàn)通過標(biāo)準(zhǔn)接口使其自身的類可見,并且沒有通過中間包裝類所導(dǎo)致的性能損失。
            由于存在的節(jié)點類型數(shù)量及其訪問方法缺乏一致性,“一切都是節(jié)點”的抽象喪失了一些意義。例如, insertData 方法被用來設(shè)置
          CharacterData 節(jié)點的值,而通過使用 setValue 方法來設(shè)置 Attr
          (屬性)節(jié)點的值。由于對于不同的節(jié)點存在不同的接口,模型的一致性和完美性降低了,而學(xué)習(xí)曲線增加了。
            JDOM
            JDOM 是使 DOM API 適應(yīng) Java 的研究計劃,從而提供了更自然和易用的接口。由于認(rèn)識到語言無關(guān) DOM 構(gòu)造的棘手本質(zhì),JDOM 目標(biāo)在于使用內(nèi)嵌的 Java 表示和對象,并且為常用任務(wù)提供便利的函數(shù)。
            例如,JDOM 直接處理“一切都是節(jié)點”和 DOM 特定構(gòu)造的使用(如 NodeList )。JDOM 將不同的節(jié)點類型(如
          Document 、 Element 和 Attribute )定義為不同的 Java 類,這意味著開發(fā)者可以使用 new
          構(gòu)造它們,避免頻繁類型轉(zhuǎn)換的需要。JDOM 將字符串表示成 Java String ,并且通過普通的 List 和 Iterator
          類來表示節(jié)點的集合。(JDOM 用其本身類替代 DOM 類。)
            JDOM 為提供更完善的接口做了相當(dāng)有益的工作。它已經(jīng)被接受成為 JSR(正式的 Java Specification
          Request),而且它將來很可能會被包含到核心的 Java 平臺中。但是,因其還不是核心 Java API
          的一部分,一些人對于使用它還心存猶豫。這兒還有關(guān)于與 Iterator 和 Java 對象頻繁創(chuàng)建相關(guān)的性能問題的報告。(請參閱 參考資料)。
            如果您對 JDOM 的接受性和可用性已經(jīng)滿足,并且如果您也沒有將 Java 代碼和程序員轉(zhuǎn)移到其它語言的直接需求,JDOM 是個值得探索的好選擇。JDOM 還不能滿足本文探討的項目所在的公司需要,因而他們使用了非常普遍的 DOM。本文也是這樣做的。
            常見編碼問題
            幾個大型 XML 項目分析揭示了使用 DOM 中的一些常見問題。下面對其中的幾個進行介紹。
            代碼臃腫
            在我們研究中查看的所有項目,本身都出現(xiàn)一個突出的問題:花費許多行代碼行來做簡單的事情。在某個示例中,使用 16
          行代碼檢查一個屬性的值。而同樣的任務(wù),帶有改進的健壯性和出錯處理,可以使用 3 行代碼實現(xiàn)。DOM API
          的低級本質(zhì)、方法和編程模式的不正確應(yīng)用以及缺乏完整 API 的知識,都會致使代碼行數(shù)增加。下面的摘要介紹了關(guān)于這些問題的特定實例。
            遍歷 DOM
            在我們探討的代碼中,最常見的任務(wù)是遍歷或搜索 DOM。 清單 1 演示了需要在文檔的 config 節(jié)里查找一個稱為“header”節(jié)點的濃縮版本代碼:
            清單 1 中,從根開始通過檢索頂端元素遍歷文檔,獲取其第一個子節(jié)點( configNode ),并且最終單獨檢查 configNode 的子節(jié)點。不幸的是,這種方法不僅冗長,而且還伴隨著脆弱性和潛在的錯誤。
            例如,第二行代碼通過使用 getFirstChild 方法獲取中間的 config
          節(jié)點。已經(jīng)存在許多潛在的問題。根節(jié)點的第一個子節(jié)點實際上可能并不是用戶正在搜索的節(jié)點。由于盲目地跟隨第一個子節(jié)點,我忽視了標(biāo)記的實際名稱并且可能
          搜索不正確的文檔部分。當(dāng)源 XML 文檔的根節(jié)點后包含空格或回車時,這種情況中發(fā)生一個頻繁的錯誤;根節(jié)點的第一個子節(jié)點實際是
          Node.TEXT_NODE 節(jié)點,而不是所希望的元素節(jié)點。您可以自己試驗一下,從 參考資料下載樣本代碼并且編輯 sample.xml 文件
          ― 在 sample 和 config 標(biāo)記之間放置一個回車。代碼立即異常而終止。要正確瀏覽所希望的節(jié)點,需要檢查每個 root
          的子節(jié)點,直到找到非 Text 的節(jié)點,并且那個節(jié)點有我正在查找的名稱為止。
            清單 1 還忽視了文檔結(jié)構(gòu)可能與我們期望有所不同的可能性。例如,如果 root 沒有任何子節(jié)點, configNode 將會被設(shè)置為
          null
          ,并且示例的第三行將產(chǎn)生一個錯誤。因此,要正確瀏覽文檔,不僅要單獨檢查每個子節(jié)點以及核對相應(yīng)的名稱,而且每步都得檢查以確保每個方法調(diào)用返回的是一
          個有效值。編寫能夠處理任意輸入的健壯、無錯的代碼,不僅需要非常關(guān)注細(xì)節(jié),而且需要編寫很多行代碼。
            最終,如果最初的開發(fā)者了解它的話,清單 1 中示例的所有功能應(yīng)該可以通過利用對 getElementsByTagName 函數(shù)的簡單調(diào)用實現(xiàn)。這是下面要討論的。
            檢索元素中的文本值
            在所分析的項目中,DOM 遍歷以后,第二項最常進行的任務(wù)是檢索在元素中包含的文本值。考慮 XML 片段 The Value 。如果已經(jīng)導(dǎo)航到 sometag 節(jié)點,如何獲取其文本值( The Value )呢?一個直觀的實現(xiàn)可能是:
            sometagElement.getData();
            正如您所猜測到的,上面的代碼并不會執(zhí)行我們希望的動作。由于實際的文本被存儲為一個或多個子節(jié)點,因此不能對 sometag 元素調(diào)用 getData 或類似的函數(shù)。更好的方法可能是:
            sometag.getFirstChild().getData();
            第二種嘗試的問題在于值實際上可能并不包含在第一個子節(jié)點中;在 sometag
          內(nèi)可能會發(fā)現(xiàn)處理指令或其它嵌入的節(jié)點,或是文本值包含在幾個子節(jié)點而不是單單一個子節(jié)點中??紤]到空格經(jīng)常作為文本節(jié)點表示,因此對
          sometag.getFirstChild() 的調(diào)用可能僅讓您得到標(biāo)記和值之間的回車。實際上,您需要遍歷所有子節(jié)點,以核對
          Node.TEXT_NODE 類型的節(jié)點,并且整理它們的值直到有完整的值為止。
            注意,JDOM 已經(jīng)利用便利的函數(shù) getText 為我們解決了這個問題。DOM 級別 3 也將有一個使用規(guī)劃的 getTextContent 方法的解答。教訓(xùn):盡可能使用較高級的 API 是不會錯的。
            getElementsByTagName
            DOM 級別 2 接口包含一個查找給定名稱的子節(jié)點的方法。例如,調(diào)用:
            NodeList names = someElement.getElementsByTagName("name");
            將返回一個包含在 someElement 節(jié)點中稱為 names 的節(jié)點 NodeList 。這無疑比我所討論的遍歷方法更方便。這也是一組常見錯誤的原因。
            問題在于 getElementsByTagName
          遞歸地遍歷文檔,從而返回所有匹配的節(jié)點。假定您有一個包含客戶信息、公司信息和產(chǎn)品信息的文檔。所有這三個項中都可能含有 name 標(biāo)記。如果調(diào)用
          getElementsByTagName
          搜索客戶名稱,您的程序極有可能行為失常,除了檢索出客戶名稱,還會檢索出產(chǎn)品和公司名稱。在文檔的子樹上調(diào)用該函數(shù)可能會降低風(fēng)險,但由于 XML
          的靈活本質(zhì),使確保您所操作的子樹包含您期望的結(jié)構(gòu),且沒有您正在搜索的名稱的虛假子節(jié)點就變得十分困難。
            DOM 的有效使用
            考慮到由 DOM 設(shè)計強加的限制,如何才能有效和高效的使用該規(guī)范呢?下面是使用 DOM 的幾條基本原則和方針,以及使工作更方便的函數(shù)庫。
            基本原則
            如果您遵循幾條基本原則,您使用 DOM 的經(jīng)驗將會顯著提高:
            ◆ 不要使用 DOM 遍歷文檔。
            ◆ 盡可能使用 XPath 來查找節(jié)點或遍歷文檔。
            ◆ 使用較高級的函數(shù)庫來更方便地使用 DOM。
            這些原則直接從對常見問題的研究中得到。正如上面所討論的,DOM 遍歷是出錯的主要原因。但它也是最常需要的功能之一。如何通過不使用 DOM 而遍歷文檔呢?
            Path
            XPath 是尋址、搜索和匹配文檔的各個部分的語言。它是 W3C 推薦標(biāo)準(zhǔn)(Recommendation),并且在大多數(shù)語言和
          XML 包中實現(xiàn)。您的 DOM 包可能直接支持 XPath 或通過加載件(add-on)支持。本文的樣本代碼對于 XPath 支持使用
          Xalan 包。
            XPath 使用路徑標(biāo)記法來指定和匹配文檔的各個部分,該標(biāo)記法與文件系統(tǒng)和 URL 中使用的類似。例如,XPath: /x/y/z 搜索文檔的根節(jié)點 x ,其下存在節(jié)點 y ,其下存在節(jié)點 z 。該語句返回與指定路徑結(jié)構(gòu)匹配的所有節(jié)點。
            更為復(fù)雜的匹配可能同時在包含文檔的結(jié)構(gòu)方面以及在節(jié)點及其屬性的值中。語句 /x/y/* 返回父節(jié)點為 x 的 y 節(jié)點下的任何節(jié)點。
          /x/y[@name=''a''] 匹配所有父節(jié)點為 x 的 y 節(jié)點,其屬性稱為 name ,屬性值為 a 。請注意,XPath
          處理篩選空格文本節(jié)點以獲得實際的元素節(jié)點 ― 它只返回元素節(jié)點。
            詳細(xì)探討 XPath 及其用法超出了本文的范圍。請參閱 參考資料獲得一些優(yōu)秀教程的鏈接?;c時間學(xué)習(xí) XPath,您將會更方便的處理 XML 文檔。
            函數(shù)庫
            當(dāng)研究 DOM
          項目時令我們驚奇的一個發(fā)現(xiàn),是存在的拷貝和粘貼代碼的數(shù)量。為什么有經(jīng)驗的開發(fā)者沒有使用良好的編程習(xí)慣,卻使用拷貝和粘貼方法而不是創(chuàng)建助手
          (helper)庫呢?我們相信這是由于 DOM
          的復(fù)雜性加深了學(xué)習(xí)的難度,并使開發(fā)者要理解能完成他們所需要的第一段代碼。開發(fā)產(chǎn)生構(gòu)成助手庫規(guī)范的函數(shù)所需的專門技術(shù)需要花費大量的時間。
            要節(jié)省一些走彎路的時間,這里是一些將使您自己的庫可以運轉(zhuǎn)起來的基本助手函數(shù)。
            findValue
            使用 XML
          文檔時,最常執(zhí)行的操作是查找給定節(jié)點的值。正如上所討論的,在遍歷文檔以查找期望的值和檢索節(jié)點的值中都出現(xiàn)難度。可以通過使用 XPath
          來簡化遍歷,而值的檢索可以一次編碼然后重用。在兩個較低級函數(shù)的幫助下,我們實現(xiàn)了 getValue 函數(shù),這兩個低級函數(shù)是:由 Xalan
          包提供的 XPathAPI.selectSingleNode (用來查找和返回與給定的 XPath 表達式匹配的第一個節(jié)點);以及
          getTextContents ,它非遞歸地返回包含在節(jié)點中的連續(xù)文本值。請注意,JDOM 的 getText 函數(shù),或?qū)⒊霈F(xiàn)在 DOM 級別
          3 中規(guī)劃的 getTextContent 方法,都可用來代替 getTextContents 。 清單
          2包含了一個簡化的清單;您可以通過下載樣本代碼來訪問所有函數(shù)(請參閱 參考資料)。
            通過同時傳入要開始搜索的節(jié)點和指定要搜索節(jié)點的 XPath 語句來調(diào)用 findValue 。函數(shù)查找第一個與給定 XPath 匹配的節(jié)點,并且抽取其文本值。
            setValue
            另一項常用的操作是將節(jié)點的值設(shè)置為希望的值,如 清單 3 所示。該函數(shù)獲取一個起始節(jié)點和一條 XPath 語句 ― 就象
          findValue ―
          以及一個用來設(shè)置匹配的節(jié)點值的字符串。它查找希望的節(jié)點,除去其所有子節(jié)點(因此除去包含在其中的任何文本和其它元素),并將其文本內(nèi)容設(shè)置為傳入的
          (passed-in)字符串。
            appendNode
            雖然某些程序查找和修改包含在 XML 文檔中的值,而另一些則通過添加和除去節(jié)點來修改文檔本身的結(jié)構(gòu)。這個助手函數(shù)簡化了文檔節(jié)點的添加,如 清單 4所示。
            該函數(shù)的參數(shù)有:要將新節(jié)點添加到其下的節(jié)點,要添加的新節(jié)點名稱,以及指定要將節(jié)點添加到其下位置的 XPath 語句(也就是,新節(jié)點的父節(jié)點應(yīng)當(dāng)是哪個)。新節(jié)點被添加到文檔的指定位置。
            最終分析
            DOM 的語言無關(guān)性設(shè)計為其帶來了非常廣泛的可應(yīng)用性并使其在大量的系統(tǒng)和平臺上得以實現(xiàn)。這樣做的代價是:使 DOM 比為每個語言專門設(shè)計的 API 更困難且更缺乏直觀性。
            DOM 奠定了一個非常有效的基礎(chǔ),遵循一些簡單的原則就可其上構(gòu)建易于使用的系統(tǒng)。凝結(jié)了一大群用戶智慧和經(jīng)驗的 DOM
          未來版本正在設(shè)計之中,而且極有可能為這里討論的問題提供解決方案。如 JDOM 這樣的項目正在修改該 API 以獲得更自然 Java
          感覺,而且如本文中所述的技術(shù)可以幫助您使 XML 的操縱更方便、更簡潔并且不易出錯。利用這些項目且遵循這些用法模式以允許 DOM 成為基于
          XML 項目的出色平臺。

          posted on 2009-04-28 17:27 liyang 閱讀(779) 評論(0)  編輯  收藏 所屬分類: xml

          主站蜘蛛池模板: 凌源市| 江永县| 龙口市| 遵义县| 兖州市| 扶余县| 阜城县| 西充县| 南投市| 敖汉旗| 桐柏县| 叶城县| 阿拉善右旗| 托里县| 山阴县| 栾城县| 垦利县| 宜丰县| 洪泽县| 岢岚县| 保定市| 陕西省| 宁南县| 南阳市| 科技| 札达县| 原阳县| 邢台县| 澄江县| 岱山县| 芦山县| 武乡县| 佛冈县| 乐东| 辰溪县| 察隅县| 集安市| 福安市| 蒲江县| 玉田县| 栾城县|