<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

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

    在Java中使用DOM和XPath進(jìn)行有效的XML處理

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

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

    主站蜘蛛池模板: 男女一进一出抽搐免费视频 | 亚洲AV无码一区二区三区久久精品 | 亚洲第一成人在线| 国产又黄又爽又刺激的免费网址 | 久久精品亚洲中文字幕无码网站 | 99精品一区二区免费视频| 亚洲熟妇无码一区二区三区 | 亚洲中文字幕无码中文| 国产亚洲精品AA片在线观看不加载| 日本免费污片中国特一级| 亚洲精华液一二三产区| 亚洲av无码专区在线播放| 精品免费国产一区二区三区| 国产一精品一av一免费爽爽| 亚洲欧美日韩中文二区| 久久亚洲成a人片| 亚洲Av无码乱码在线播放| 在线视频精品免费| baoyu116.永久免费视频| 亚洲av无码日韩av无码网站冲| 亚洲精品无码av人在线观看 | 91亚洲国产在人线播放午夜| 亚洲一级片免费看| 好爽…又高潮了毛片免费看| 无码国产精品一区二区免费式芒果 | 国产一二三四区乱码免费| 亚洲欧美成人av在线观看| 综合自拍亚洲综合图不卡区| 亚洲中文字幕视频国产| 免费无码又爽又高潮视频 | 伊人婷婷综合缴情亚洲五月| 免费看香港一级毛片| 又粗又大又黑又长的免费视频| 日韩精品无码免费专区午夜| 老司机午夜性生免费福利 | 亚洲av无码专区国产乱码在线观看 | 成人福利在线观看免费视频| 亚洲熟妇丰满xxxxx| 亚洲日韩国产精品无码av| 亚洲av网址在线观看| 亚洲精品无码国产|