溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

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

發(fā)布時(shí)間:2021-11-03 10:19:30 來源:億速云 閱讀:94 作者:小新 欄目:編程語言

這篇文章主要介紹在Java中如何使用DOM和XPath進(jìn)行有效的XML處理,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!

文檔對(duì)象模型

DOM 規(guī)范被設(shè)計(jì)成可與任何編程語言一起使用。因此,它嘗試使用在所有語言中都可用的一組通用的、核心的功能部件。DOM 規(guī)范同樣嘗試保持其接口定義方面的無關(guān)性。這就允許 Java 程序員在使用 Visual Basic 或 Perl 時(shí)應(yīng)用他們的 DOM 知識(shí),反之亦然。

該規(guī)范同樣將文檔的每個(gè)部分看成由類型和值組成的節(jié)點(diǎn)。這為處理文檔的所有方面提供了***的概念性框架。例如,下面的 XML 片段

the Italicized portion.

就是通過以下的 DOM 結(jié)構(gòu)表示的:

圖 1:XML 文檔的 DOM 表示

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

樹的每個(gè)Document 、 Element 、 Text 和 Attr 部分都是 DOM Node 。

***的抽象確實(shí)付出了代價(jià)??紤] XML 片段: Value 。您或許會(huì)認(rèn)為文本的值可以通過普通的 Java String 對(duì)象來表示,并且通過簡單的 getValue 調(diào)用可訪問。實(shí)際上,文本被當(dāng)成 tagname 節(jié)點(diǎn)下的一個(gè)或多個(gè)子 Node 。因此,為了獲取文本值,您需要遍歷 tagname 的子節(jié)點(diǎn),將每個(gè)值整理成一個(gè)字符串。這樣做有充分的理由: tagname 可能包含其它嵌入的 XML 元素,在這種情況下獲取其文本值沒有多大意義。然而,在現(xiàn)實(shí)世界中,我們看到由于缺乏便利的函數(shù)導(dǎo)致頻繁的編碼錯(cuò)誤占了 80% 的情況,這樣做的確有意義。

設(shè)計(jì)問題

DOM 語言無關(guān)性的缺點(diǎn)是通常在每個(gè)編程語言中使用的一整套工作方法和模式不能被使用。例如,不能使用熟悉的 Java new 構(gòu)造創(chuàng)建新的 Element ,開發(fā)者必須使用工廠構(gòu)造器方法。 Node 的集合被表示成 NodeList ,而不是通常的 List 或 Iterator 對(duì)象。這些微小的不便意味著不同尋常的編碼實(shí)踐和增多的代碼行,并且它們迫使程序員學(xué)習(xí) DOM 的行事方法而不是用直覺的方法。

DOM 使用“一切都是節(jié)點(diǎn)”的抽象。這就意味著幾乎 XML 文檔的每個(gè)部分,例如: Document 、 Element 和 Attr ,全都繼承( extend ) Node 接口。這不僅是概念上***,而且還允許每個(gè) DOM 的不同實(shí)現(xiàn)通過標(biāo)準(zhǔn)接口使其自身的類可見,并且沒有通過中間包裝類所導(dǎo)致的性能損失。

由于存在的節(jié)點(diǎn)類型數(shù)量及其訪問方法缺乏一致性,“一切都是節(jié)點(diǎn)”的抽象喪失了一些意義。例如, insertData 方法被用來設(shè)置 CharacterData 節(jié)點(diǎn)的值,而通過使用 setValue 方法來設(shè)置 Attr (屬性)節(jié)點(diǎn)的值。由于對(duì)于不同的節(jié)點(diǎn)存在不同的接口,模型的一致性和***性降低了,而學(xué)習(xí)曲線增加了。

JDOM

JDOM 是使 DOM API 適應(yīng) Java 的研究計(jì)劃,從而提供了更自然和易用的接口。由于認(rèn)識(shí)到語言無關(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 類,這意味著開發(fā)者可以使用 new 構(gòu)造它們,避免頻繁類型轉(zhuǎn)換的需要。JDOM 將字符串表示成 Java String ,并且通過普通的 List 和 Iterator 類來表示節(jié)點(diǎn)的集合。(JDOM 用其本身類替代 DOM 類。)

JDOM 為提供更完善的接口做了相當(dāng)有益的工作。它已經(jīng)被接受成為 JSR(正式的 Java Specification Request),而且它將來很可能會(huì)被包含到核心的 Java 平臺(tái)中。但是,因其還不是核心 Java API 的一部分,一些人對(duì)于使用它還心存猶豫。這兒還有關(guān)于與 Iterator 和 Java 對(duì)象頻繁創(chuàng)建相關(guān)的性能問題的報(bào)告。(請(qǐng)參閱 參考資料)。

如果您對(duì) JDOM 的接受性和可用性已經(jīng)滿足,并且如果您也沒有將 Java 代碼和程序員轉(zhuǎn)移到其它語言的直接需求,JDOM 是個(gè)值得探索的好選擇。JDOM 還不能滿足本文探討的項(xiàng)目所在的公司需要,因而他們使用了非常普遍的 DOM。本文也是這樣做的。

常見編碼問題

幾個(gè)大型 XML 項(xiàng)目分析揭示了使用 DOM 中的一些常見問題。下面對(duì)其中的幾個(gè)進(jìn)行介紹。

代碼臃腫

在我們研究中查看的所有項(xiàng)目,本身都出現(xiàn)一個(gè)突出的問題:花費(fèi)許多行代碼行來做簡單的事情。在某個(gè)示例中,使用 16 行代碼檢查一個(gè)屬性的值。而同樣的任務(wù),帶有改進(jìn)的健壯性和出錯(cuò)處理,可以使用 3 行代碼實(shí)現(xiàn)。DOM API 的低級(jí)本質(zhì)、方法和編程模式的不正確應(yīng)用以及缺乏完整 API 的知識(shí),都會(huì)致使代碼行數(shù)增加。下面的摘要介紹了關(guān)于這些問題的特定實(shí)例。

遍歷 DOM

在我們探討的代碼中,最常見的任務(wù)是遍歷或搜索 DOM。 清單 1 演示了需要在文檔的 config 節(jié)里查找一個(gè)稱為“header”節(jié)點(diǎn)的濃縮版本代碼:

清單 1 中,從根開始通過檢索頂端元素遍歷文檔,獲取其***個(gè)子節(jié)點(diǎn)( configNode ),并且最終單獨(dú)檢查 configNode 的子節(jié)點(diǎn)。不幸的是,這種方法不僅冗長,而且還伴隨著脆弱性和潛在的錯(cuò)誤。

例如,第二行代碼通過使用 getFirstChild 方法獲取中間的 config 節(jié)點(diǎn)。已經(jīng)存在許多潛在的問題。根節(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 沒有任何子節(jié)點(diǎn), configNode 將會(huì)被設(shè)置為 null ,并且示例的第三行將產(chǎn)生一個(gè)錯(cuò)誤。因此,要正確瀏覽文檔,不僅要單獨(dú)檢查每個(gè)子節(jié)點(diǎn)以及核對(duì)相應(yīng)的名稱,而且每步都得檢查以確保每個(gè)方法調(diào)用返回的是一個(gè)有效值。編寫能夠處理任意輸入的健壯、無錯(cuò)的代碼,不僅需要非常關(guān)注細(xì)節(jié),而且需要編寫很多行代碼。

最終,如果最初的開發(fā)者了解它的話,清單 1 中示例的所有功能應(yīng)該可以通過利用對(duì) getElementsByTagName 函數(shù)的簡單調(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();

正如您所猜測到的,上面的代碼并不會(huì)執(zhí)行我們希望的動(dòng)作。由于實(shí)際的文本被存儲(chǔ)為一個(gè)或多個(gè)子節(jié)點(diǎn),因此不能對(duì) sometag 元素調(diào)用 getData 或類似的函數(shù)。更好的方法可能是:

sometag.getFirstChild().getData();

第二種嘗試的問題在于值實(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è)問題。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 。這無疑比我所討論的遍歷方法更方便。這也是一組常見錯(cuò)誤的原因。

問題在于 getElementsByTagName 遞歸地遍歷文檔,從而返回所有匹配的節(jié)點(diǎn)。假定您有一個(gè)包含客戶信息、公司信息和產(chǎn)品信息的文檔。所有這三個(gè)項(xiàng)中都可能含有 name 標(biāo)記。如果調(diào)用 getElementsByTagName 搜索客戶名稱,您的程序極有可能行為失常,除了檢索出客戶名稱,還會(huì)檢索出產(chǎn)品和公司名稱。在文檔的子樹上調(diào)用該函數(shù)可能會(huì)降低風(fēng)險(xiǎn),但由于 XML 的靈活本質(zhì),使確保您所操作的子樹包含您期望的結(jié)構(gòu),且沒有您正在搜索的名稱的虛假子節(jié)點(diǎn)就變得十分困難。

DOM 的有效使用

考慮到由 DOM 設(shè)計(jì)強(qiáng)加的限制,如何才能有效和高效的使用該規(guī)范呢?下面是使用 DOM 的幾條基本原則和方針,以及使工作更方便的函數(shù)庫。

基本原則

如果您遵循幾條基本原則,您使用 DOM 的經(jīng)驗(yàn)將會(huì)顯著提高:

◆ 不要使用 DOM 遍歷文檔。

◆ 盡可能使用 XPath 來查找節(jié)點(diǎn)或遍歷文檔。

◆ 使用較高級(jí)的函數(shù)庫來更方便地使用 DOM。

這些原則直接從對(duì)常見問題的研究中得到。正如上面所討論的,DOM 遍歷是出錯(cuò)的主要原因。但它也是最常需要的功能之一。如何通過不使用 DOM 而遍歷文檔呢?

Path

XPath 是尋址、搜索和匹配文檔的各個(gè)部分的語言。它是 W3C 推薦標(biāo)準(zhǔn)(Recommendation),并且在大多數(shù)語言和 XML 包中實(shí)現(xiàn)。您的 DOM 包可能直接支持 XPath 或通過加載件(add-on)支持。本文的樣本代碼對(duì)于 XPath 支持使用 Xalan 包。

XPath 使用路徑標(biāo)記法來指定和匹配文檔的各個(gè)部分,該標(biāo)記法與文件系統(tǒng)和 URL 中使用的類似。例如,XPath: /x/y/z 搜索文檔的根節(jié)點(diǎn) x ,其下存在節(jié)點(diǎn) y ,其下存在節(jié)點(diǎn) z 。該語句返回與指定路徑結(jié)構(gòu)匹配的所有節(jié)點(diǎn)。

更為復(fù)雜的匹配可能同時(shí)在包含文檔的結(jié)構(gòu)方面以及在節(jié)點(diǎn)及其屬性的值中。語句 /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)秀教程的鏈接?;c(diǎn)時(shí)間學(xué)習(xí) XPath,您將會(huì)更方便的處理 XML 文檔。

函數(shù)庫

當(dāng)研究 DOM 項(xiàng)目時(shí)令我們驚奇的一個(gè)發(fā)現(xiàn),是存在的拷貝和粘貼代碼的數(shù)量。為什么有經(jīng)驗(yàn)的開發(fā)者沒有使用良好的編程習(xí)慣,卻使用拷貝和粘貼方法而不是創(chuàng)建助手(helper)庫呢?我們相信這是由于 DOM 的復(fù)雜性加深了學(xué)習(xí)的難度,并使開發(fā)者要理解能完成他們所需要的***段代碼。開發(fā)產(chǎn)生構(gòu)成助手庫規(guī)范的函數(shù)所需的專門技術(shù)需要花費(fèi)大量的時(shí)間。

要節(jié)省一些走彎路的時(shí)間,這里是一些將使您自己的庫可以運(yùn)轉(zhuǎn)起來的基本助手函數(shù)。

findValue

使用 XML 文檔時(shí),最常執(zhí)行的操作是查找給定節(jié)點(diǎn)的值。正如上所討論的,在遍歷文檔以查找期望的值和檢索節(jié)點(diǎn)的值中都出現(xiàn)難度??梢酝ㄟ^使用 XPath 來簡化遍歷,而值的檢索可以一次編碼然后重用。在兩個(gè)較低級(jí)函數(shù)的幫助下,我們實(shí)現(xiàn)了 getValue 函數(shù),這兩個(gè)低級(jí)函數(shù)是:由 Xalan 包提供的 XPathAPI.selectSingleNode (用來查找和返回與給定的 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 方法,都可用來代替 getTextContents 。 清單 2包含了一個(gè)簡化的清單;您可以通過下載樣本代碼來訪問所有函數(shù)(請(qǐng)參閱 參考資料)。

通過同時(shí)傳入要開始搜索的節(jié)點(diǎn)和指定要搜索節(jié)點(diǎn)的 XPath 語句來調(diào)用 findValue 。函數(shù)查找***個(gè)與給定 XPath 匹配的節(jié)點(diǎn),并且抽取其文本值。

setValue

另一項(xiàng)常用的操作是將節(jié)點(diǎn)的值設(shè)置為希望的值,如 清單 3 所示。該函數(shù)獲取一個(gè)起始節(jié)點(diǎn)和一條 XPath 語句 ― 就象 findValue ― 以及一個(gè)用來設(shè)置匹配的節(jié)點(diǎn)值的字符串。它查找希望的節(jié)點(diǎn),除去其所有子節(jié)點(diǎn)(因此除去包含在其中的任何文本和其它元素),并將其文本內(nèi)容設(shè)置為傳入的(passed-in)字符串。

appendNode

雖然某些程序查找和修改包含在 XML 文檔中的值,而另一些則通過添加和除去節(jié)點(diǎn)來修改文檔本身的結(jié)構(gòu)。這個(gè)助手函數(shù)簡化了文檔節(jié)點(diǎn)的添加,如 清單 4所示。

該函數(shù)的參數(shù)有:要將新節(jié)點(diǎn)添加到其下的節(jié)點(diǎn),要添加的新節(jié)點(diǎn)名稱,以及指定要將節(jié)點(diǎn)添加到其下位置的 XPath 語句(也就是,新節(jié)點(diǎn)的父節(jié)點(diǎn)應(yīng)當(dāng)是哪個(gè))。新節(jié)點(diǎn)被添加到文檔的指定位置。

以上是“在Java中如何使用DOM和XPath進(jìn)行有效的XML處理”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI