2008 年 3 月 03 日 在閱讀了本系列的第 1 部分和第 2 部分之后,您應該已經(jīng)熟悉如何使用 Castor 把 XML 轉換成 Java?,然后再把 Java 轉換回 XML。在本文中,將學(xué)習如何通過(guò) Castor 映射文件增加靈活性。您將不再受到 XML 文檔中的元素名或 Java 類(lèi)中的成員變量名的限制。 您現在應該具備的(回顧) 與前一篇文章一樣,本文也對您系統的設置情況和您的技能做一些假設。首先,需要按照本系列的第 1 部分中的描述下載并安裝 Castor 的最新版本,設置類(lèi)路徑和相關(guān)的 Java 庫(參見(jiàn) 參考資料 中本系列第一篇文章的鏈接)。然后,按照第 2 部分中的描述,熟悉 Castor 的基本編組和解組設施。 所以,您應該能夠使用 Castor 提取出 XML 文檔中的數據,并使用自己的 Java 類(lèi)處理數據。用數據綁定術(shù)語(yǔ)來(lái)說(shuō),這稱(chēng)為解組(unmarshalling)。反向的過(guò)程稱(chēng)為編組(marshalling):您應該能夠把 Java 類(lèi)的成員變量中存儲的數據轉換為 XML 文檔。如果您還不熟悉 Castor 的解組器和編組器,那么應該閱讀 第 2 部分(參見(jiàn) 參考資料 中的鏈接)。
非理想環(huán)境下的數據綁定 初看上去,您似乎已經(jīng)掌握了有效地使用 Castor 所需了解的所有過(guò)程:設置、編組和解組。但是,您到目前為止學(xué)到的所有東西只適用于所謂的理想環(huán)境。在這樣的環(huán)境中,每個(gè)人編寫(xiě)的 XML 都是完美的,其中的元素名是有意義的,比如 “title” 和 “authorAddress”,而不是 “t” 或 “aa”。Java 類(lèi)是按照有組織的方式創(chuàng )建的,采用單數作為類(lèi)名(比如 “Book”),采用單數名詞作為成員變量名(比如 “isbn” 和 “price”)。另外,數據類(lèi)型也是正確的:沒(méi)有開(kāi)發(fā)人員把 price 的數據類(lèi)型設置為 int 而不是 float,或者使用 char 數組存儲字符串數據(這是 C 語(yǔ)言的做法)。 但是,大多數程序員所處的環(huán)境并不完美(我真想找到一個(gè)能夠把我送到完美世界的魔法衣廚)。在大多數程序員所處的環(huán)境中有許多不理想的情況:XML 文檔常常有糟糕的元素名和屬性名,還要應付名稱(chēng)空間問(wèn)題。元素數據存儲在屬性中,一些數據甚至由管道符或分號分隔。 Java 類(lèi)是繼承的,對它們進(jìn)行重新組織在時(shí)間和工作量方面的成本可能會(huì )超過(guò)帶來(lái)的好處。這些類(lèi)常常無(wú)法簡(jiǎn)潔地映射到 XML 模式(認為 XML 和數據人員會(huì )與 Java 程序員相互妥協(xié)的想法也是非常不可思議的),而且在某些情況下,即使實(shí)現了簡(jiǎn)潔映射,也肯定不會(huì )跨所有類(lèi)和數據。XML 元素名可能不合適,許多 Java 變量名也可能不合適。甚至可能遇到使用 Hungarian 表示法的名稱(chēng),按照這種表示法,所有成員變量都以 “m” 開(kāi)頭,比如 mTitle。這很不好看。 在這些情況下,您目前學(xué)到的數據綁定方法就無(wú)能為力了。XML 文檔中可能會(huì )出現 Hungarian 風(fēng)格的元素名,Java 類(lèi)中也可能出現沒(méi)有意義的結構。這種情況是無(wú)法接受的。如果不能按照您希望的方式獲取和操作 XML 文檔的數據,那么 Castor(或任何數據綁定框架)又有什么意義呢?
靈活數據綁定的目標 首先要注意,在 Castor 或任何其他數據綁定框架中,使用映射文件都要花一些時(shí)間。必須先學(xué)習一些新語(yǔ)法。盡管映射文件使用 XML 格式(大多數框架都是這樣的),但是您需要學(xué)習一些新元素和屬性。還必須做一些測試,確保 XML 和 Java 代碼之間的相互轉換產(chǎn)生您希望的結果。最后,如果親自指定映射,而不是讓框架處理映射,就可能在數據綁定中遇到更多的錯誤。例如,如果希望讓框架把 XML 中的 fiddler 元素映射到 Java 代碼中的 violin 屬性,但是錯誤地聲明這個(gè)屬性是在 player 類(lèi)中(應該是在 Player 類(lèi)中),那么就會(huì )遇到錯誤。因此,在親自指定映射時(shí),必須非常注意拼寫(xiě)、大小寫(xiě)、下劃線(xiàn)、單引號和雙引號。 在學(xué)習使用映射文件之前,應該確定確實(shí)需要這么做。如果掌握了映射文件,但是卻不使用它,那就是浪費時(shí)間。但是,映射確實(shí)有一些優(yōu)點(diǎn)。 Java 代碼不再受 XML 命名方式的限制 前面曾經(jīng)提到,在把 XML 轉換為 Java 代碼時(shí),大小寫(xiě)可能會(huì )導致錯誤。在 XML 中,最常用的做法是名稱(chēng)全部小寫(xiě)并加連字符,比如 first-name。有時(shí)候,甚至會(huì )看到 first_name。這樣的名稱(chēng)會(huì )轉換為很難看的 Java 屬性名;沒(méi)人愿意在代碼中調用 getFirst-name()。實(shí)際上,在大多數由程序員(而不是 XML 開(kāi)發(fā)人員或數據管理員)編寫(xiě)的文檔中,往往使用駝峰式(camel-case)命名法,比如 firstName。通過(guò)使用映射文件,很容易把 XML 風(fēng)格的名稱(chēng)(比如 first-name)映射為 Java 風(fēng)格的名稱(chēng)(比如 firstName)。最棒的一點(diǎn)是,不需要強迫 XML 人員像 Java 程序員那樣思考,這往往比學(xué)習新的映射語(yǔ)法困難得多。 XML 不再受 Java 命名方式的限制 是的,這似乎很明顯。既然可以調整 XML 到 Java 的命名轉換,反過(guò)來(lái)肯定也可以:在把 Java 類(lèi)和屬性包含的數據轉換為 XML 時(shí),可以修改 Java 名稱(chēng)。但是,有一個(gè)更重要,也更微妙的好處:不再受到 Java 類(lèi)名和包名的限制。 這很可能成為一個(gè)組織問(wèn)題。例如,在大多數情況下,XML 中的嵌套元素轉換為類(lèi)結構,最內層的嵌套元素轉換成類(lèi)屬性(成員變量)??匆幌虑鍐?1 中的 XML: 清單 1. 代表圖書(shū)的 XML <?xml version="1.0" encoding="UTF-8"?> <book> <authors total-sales="0"> <last-name>Finder</last-name> <first-name>Joseph</first-name> </authors> <isbn>9780312347482</isbn> <title>Power Play</title> </book> | Castor(或任何其他數據綁定框架)可能假設您需要一個(gè) Book 類(lèi),這個(gè)類(lèi)引用幾個(gè) Author 類(lèi)實(shí)例。author 類(lèi)應該有成員變量 lastName 和 firstName(這里會(huì )出現前面提到的命名問(wèn)題,Author 中的成員變量應該是 last-name,還是 lastName?對于名字也有這個(gè)問(wèn)題)。但是,如果這不是您希望的結果,應該怎么辦?例如,您可能在一個(gè)稱(chēng)為 Person 或 Professional 的類(lèi)中存儲所有作家、會(huì )議演講人和教授。在這種情況下就真有麻煩了,而且您不會(huì )愿意全面修改 XML 元素的結構和名稱(chēng)來(lái)解決這個(gè)問(wèn)題。實(shí)際上,在這種情況下,要想原樣保持 XML,使用映射是惟一的辦法。 映射允許我們在 Java-XML 轉換的兩端指定命名方式。我們不希望由于 XML 文檔的原因修改 Java 代碼,同樣不愿意修改 XML 結構來(lái)適應 Java 類(lèi)和成員變量。另外,Java 包也會(huì )增加復雜性。盡管在 Castor 中包并不是大問(wèn)題,但是仍然必須在編組的 XML 中存儲 Java 類(lèi)和包的相關(guān)信息,這對于業(yè)務(wù)邏輯(Java 類(lèi))和數據(XML)的隔離很不利。映射可以解決所有這些問(wèn)題。 映射允許在現有環(huán)境中添加數據綁定 前兩個(gè)問(wèn)題(對 XML 和 Java 代碼的限制)實(shí)際上與一個(gè)更大的問(wèn)題相關(guān)。大多數情況下,您已經(jīng)有了一組 Java 對象和一個(gè)或多個(gè) XML 文檔。因此,不具備前兩篇文章中的那種自由度:不能讓 Castor 根據它自己的規則把 Java 代碼解組為 XML,或者為 XML 文檔生成 Java 類(lèi)。 相反,更為常見(jiàn)的情況是,您需要把一種新技術(shù) — 數據綁定 — 添加到現有的結構中。在這種情況下,映射文件就是使用數據綁定的關(guān)鍵。在兩個(gè) “端點(diǎn)”(當前的對象模型和當前的 XML 結構)固定的情況下,映射使我們仍然能夠把這兩者的數據聯(lián)系起來(lái)。簡(jiǎn)而言之,良好的映射系統使數據綁定能夠在真實(shí)環(huán)境中發(fā)揮作用,而不僅僅停留在理論上。
一個(gè)映射場(chǎng)景示例 我們先來(lái)看一個(gè)簡(jiǎn)單的映射場(chǎng)景。在前一篇文章中,我們開(kāi)發(fā)了 Book 和 Author 類(lèi)。清單 2 是 Book 類(lèi)。 清單 2. Book 類(lèi) package ibm.xml.castor; import java.util.LinkedList; import java.util.List; public class Book { /** The book‘s ISBN */ private String isbn; /** The book‘s title */ private String title; /** The authors‘ names */ private List<Author> authors; public Book() { } public Book(String isbn, String title, List<Author> authors) { this.isbn = isbn; this.title = title; this.authors = authors; } public Book(String isbn, String title, Author author) { this.isbn = isbn; this.title = title; this.authors = new LinkedList<Author>(); authors.add(author); } public void setIsbn(String isbn) { this.isbn = isbn; } public String getIsbn() { return isbn; } public void setTitle(String title) { this.title = title; } public String getTitle() { return title; } public void setAuthors(List<Author> authors) { this.authors = authors; } public List<Author> getAuthors() { return authors; } public void addAuthor(Author author) { authors.add(author); } } | 清單 3 是 Author 類(lèi)。 清單 3. Author 類(lèi) package ibm.xml.castor; public class Author { private String firstName, lastName; public Author() { } public Author(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public void setFirstName(String firstName) { this.firstName = firstName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } } | 注意:與前一篇文章相比,惟一的修改是在 Author 中刪除了總銷(xiāo)售額(totalSales 變量)。我發(fā)現它的意義不大,所以在這個(gè)版本中刪除了它。 一個(gè)比較麻煩的 XML 文檔 這一次不使用前一篇文章中的 XML,而是使用一個(gè)不太容易映射的 XML 文檔。清單 4 給出希望綁定到 清單 2 和 清單 3 中的 Java 類(lèi)的 XML 文檔。 清單 4. 用于數據綁定的 XML <?xml version="1.0" encoding="UTF-8"?> <book> <author> <name first="Douglas" last="Preston" /> </author> <author> <name first="Lincoln" last="Child" /> </author> <book-info> <isbn>9780446618502</isbn> <title>The Book of the Dead</title> </book-info> </book> | 需要映射哪些數據? 在處理映射文件語(yǔ)法或 Castor 的 API 之前,第一個(gè)任務(wù)是判斷需要把 XML(清單 4)中的哪些數據綁定到 Java 類(lèi)(清單 2 和 清單 3)。請考慮一會(huì )兒。 下面簡(jiǎn)要總結一下這個(gè) XML 文檔應該如何映射到 Java 類(lèi): book 元素應該映射到 Book 類(lèi)的一個(gè)實(shí)例。 - 每個(gè)
author 元素應該映射到 Author 類(lèi)的一個(gè)實(shí)例。 - 每個(gè)
Author 實(shí)例應該添加到 Book 實(shí)例中的 authors 列表中。 - 對于每個(gè)
Author 實(shí)例,firstName 應該設置為 name 元素上的 first 屬性的值。 - 對于每個(gè)
Author 實(shí)例,lastName 應該設置為 name 元素上的 last 屬性的值。 Book 實(shí)例的 ISBN 應該設置為 book-info 元素中嵌套的 isbn 元素的值。 Book 實(shí)例的書(shū)名應該設置為 book-info 元素中嵌套的 title 元素的值。 其中一些映射您已經(jīng)知道如何實(shí)現了。例如,book 到 Book 類(lèi)實(shí)例的映射是標準的,Castor 會(huì )默認處理這個(gè)任務(wù)。但是,也有一些新東西,比如說(shuō)作者。盡管把一個(gè) author 元素映射到一個(gè) Author 實(shí)例沒(méi)什么問(wèn)題,但是沒(méi)有分組元素,比如 authors,它清楚地顯示出所有作者屬于 Book 實(shí)例中的一個(gè)集合。 這里還有一些元素和屬性并不直接映射到 Java 對象模型。Author 類(lèi)包含表示名字和 姓氏的變量,但是在 XML 中只用一個(gè)元素(name)表示作者姓名,這個(gè)元素有兩個(gè)屬性。book-info 元素中嵌套的書(shū)名和 ISBN 并不映射到任何 Java 對象。 這種情況非常適合使用映射文件。它使我們能夠使用這種 XML(包含我們需要的數據,但是結構不符合希望),仍然能夠把文檔中的數據放到 Java 對象中。而且,映射文件本身并不難編寫(xiě)。
基本的映射文件 Castor 中的映射是通過(guò)使用映射文件(mapping file) 實(shí)現的。映射文件僅僅是一個(gè) XML 文檔,它提供了如何在 Java 代碼和 XML 之間進(jìn)行轉換的相關(guān)信息。因為您熟悉 XML,所以您會(huì )發(fā)現編寫(xiě)映射文件是非常容易的。實(shí)際上,對于簡(jiǎn)單的映射(只需修改元素名和 Java 類(lèi)或成員變量的名稱(chēng)),只需一點(diǎn)兒時(shí)間就能編寫(xiě)好映射文件。 然后,當進(jìn)行編組和解組時(shí)(前兩篇文章已經(jīng)介紹過(guò)如何在程序中進(jìn)行編組和解組),Castor 會(huì )使用這個(gè)文件。只需對 Castor 多做一個(gè) API 調用;其他代碼都是一樣的。 mapping 元素 Castor 映射文件的開(kāi)始是一個(gè)普通的 XML 文檔,然后是根元素 mapping。還可以引用 Castor 映射 DTD。這樣就可以檢驗文檔,確保結構和語(yǔ)法沒(méi)有任何問(wèn)題。這會(huì )大大簡(jiǎn)化對映射的調試。 清單 5 給出最基本的映射文件。 清單 5. 基本的 Castor 映射文件 <?xml version="1.0"?> <!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN" "http://castor.org/mapping.dtd"> <mapping> <!-- All your mappings go here --> </mapping> | 這個(gè)文件顯然沒(méi)有實(shí)質(zhì)性?xún)热?,但它是所有映射文件的起點(diǎn)。 用 class 元素對類(lèi)進(jìn)行映射 建立基本的映射文件之后,差不多總是先要把一個(gè) Java 類(lèi)映射到一個(gè) XML 元素。在這個(gè)示例中,需要把 Book 類(lèi)映射到 book 元素中的數據。映射文件首先考慮類(lèi),所以需要添加一個(gè) class 元素,并在這個(gè)元素的 name 屬性中指定完全限定的 Java 類(lèi)名,比如: <?xml version="1.0"?> <!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN" "http://castor.org/mapping.dtd"> <mapping> <class name="ibm.xml.castor.Book"> </class> </mapping> | 現在,可以使用 map-to 元素和 xml 屬性指定這個(gè)類(lèi)要映射到的 XML 元素。這個(gè)元素嵌套在 XML 元素映射到的 Java 類(lèi)(完全限定,包括包名)的 class 元素中,比如: <?xml version="1.0"?> <!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN" "http://castor.org/mapping.dtd"> <mapping> <class name="ibm.xml.castor.Book"> <map-to xml="book" /> </class> </mapping> | 這非常簡(jiǎn)單。實(shí)際上,到目前為止,這個(gè)映射文件只實(shí)現 Castor 的默認操作。除非由于以下兩個(gè)原因,否則可以刪除這個(gè)部分并讓 Castor 處理這個(gè)任務(wù): - 需要指定如何填充
Book 中的某些字段,比如書(shū)名和 ISBN。 - 如果使用映射文件,那么最好指定所有內容 的映射方式。這會(huì )更明確 XML 和 Java 代碼之間的配合。
把字段映射到元素 有了基本的類(lèi)到元素的映射之后,就可以開(kāi)始把 Book 類(lèi)的字段映射到 XML 文檔中的特定元素。Castor 映射文件使用 field 元素指定要使用的 Java 成員變量,使用其中嵌套的 bind-xml 元素指定映射到的 XML 元素。因此,下面的代碼指定把 Book 類(lèi)中的 title 變量映射到 book 元素中嵌套的 title 元素: <?xml version="1.0"?> <!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN" "http://castor.org/mapping.dtd"> <mapping> <class name="ibm.xml.castor.Book"> <map-to xml="book" /> <field name="Title" type="java.lang.String"> <bind-xml name="title" node="element" /> </field> </class> </mapping> | | 屬性名使代碼隔離 使用屬性名(屬性名將轉換為方法名)的真正價(jià)值在于,隱藏了類(lèi)的細節。無(wú)論類(lèi)的成員變量命名為 title、book-title、mTitle 還是 _title,只要有一個(gè) setTitle() 方法,Castor 就接受簡(jiǎn)單的屬性名 “title”。這就允許使用多種編程風(fēng)格,而對類(lèi)的處理只依賴(lài)于它的公共 API:它的 getter 和 setter。 | | 在這里要注意兩點(diǎn)。首先,提供了屬性名(在這個(gè)示例中是 “title”)。這是屬性(property) 名,而不是成員變量名。換句話(huà)說(shuō),Castor 通過(guò)調用 set[PropertyName]() 來(lái)使用這個(gè)屬性名。如果提供屬性名 “foo”,Castor 就會(huì )嘗試調用 setfoo() — 這可能不是您希望的情況。因此,要仔細注意大小寫(xiě),并使用屬性 名而不是 Java 類(lèi)中的變量名。 要注意的第二點(diǎn)是 type 屬性。這個(gè)屬性向 Castor 說(shuō)明數據究竟是什么類(lèi)型的。在這個(gè)示例中,這很簡(jiǎn)單;但是在某些情況下,希望將以 XML 文本式數據存儲的數字存儲為整數、小數或者只是字符串,這時(shí)指定正確的數據類(lèi)型就很重要了。另外,類(lèi)型應該使用完全限定的 Java 類(lèi)名,比如 java.lang.String。 在 bind-xml 元素中,指定要綁定到的 XML 元素的名稱(chēng)(在這個(gè)示例中是 “title”),并使用 node 屬性指定是綁定到元素還是綁定到屬性。這樣就可以輕松地使用元素和屬性數據,只需在映射文件中稍做修改即可。 指定 XML 元素的位置 但是,這里需要解決一個(gè)問(wèn)題:書(shū)名和 ISBN 嵌套在 book-info 元素中,而不是直接嵌套在 book 元素中。所以需要在映射文件中指出這一情況。 當遇到這種情況時(shí) — 一個(gè)類(lèi)中的一個(gè)字段并不直接映射到與這個(gè)類(lèi)對應的 XML 元素中的數據 — 就需要在 bind-xml 元素中使用 location 屬性。這個(gè)屬性的值應該是 XML 文檔中包含您希望綁定到的數據的元素。如果綁定到元素數據,它就應該是目標元素的父 元素;如果綁定到屬性數據,它就應該是包含這個(gè)屬性的 元素。 因此,在這個(gè)示例中,希望把 Book 類(lèi)的書(shū)名屬性綁定到 title 元素的值,而這個(gè)元素嵌套在 book-info 元素中。下面是映射方法: <?xml version="1.0"?> <!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN" "http://castor.org/mapping.dtd"> <mapping> <class name="ibm.xml.castor.Book"> <map-to xml="book" /> <field name="Title" type="java.lang.String"> <bind-xml name="title" node="element" location="book-info" </field> </class> </mapping> | 然后為書(shū)的 ISBN 添加另一個(gè)字段映射: <?xml version="1.0"?> <!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN" "http://castor.org/mapping.dtd"> <mapping> <class name="ibm.xml.castor.Book"> <map-to xml="book" /> <field name="Title" type="java.lang.String"> <bind-xml name="title" node="element" location="book-info" /> </field> <field name="Isbn" type="java.lang.String"> <bind-xml name="isbn" node="element" location="book-info" /> </field> </class> </mapping> | 現在,Book 類(lèi)的屬性都已經(jīng)設置好了。該處理 Author 類(lèi)了。
對其他類(lèi)進(jìn)行映射 按照相同的方式對其他類(lèi)進(jìn)行映射。惟一的差異是在其他類(lèi)中不需要使用 map-to 元素。 對 Author 類(lèi)進(jìn)行映射 需要把 Author 類(lèi)中的字段映射到 author 元素。請記住,下面是要處理的 XML 片段: <author> <name first="Lincoln" last="Child" /> </author> | 惟一需要注意的是,這里并不用兩個(gè)元素分別包含名字和姓氏,而是使用一個(gè)帶兩個(gè)屬性的元素。但是,前面已經(jīng)使用過(guò) location 屬性(需要用這個(gè)屬性指定 name 元素是映射到的位置)和 node 屬性(可以在這里指定要綁定到屬性數據,而不是元素)。所以在映射文件中需要以下代碼: <?xml version="1.0"?> <!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN" "http://castor.org/mapping.dtd"> <mapping> <class name="ibm.xml.castor.Book"> <map-to xml="book" /> <field name="Title" type="java.lang.String"> <bind-xml name="title" node="element" location="book-info" /> </field> <field name="Isbn" type="java.lang.String"> <bind-xml name="isbn" node="element" location="book-info" /> </field> </class> <class name="ibm.xml.castor.Author"> <field name="FirstName" type="java.lang.String"> <bind-xml name="first" node="attribute" location="name" /> </field> <field name="LastName" type="java.lang.String"> <bind-xml name="last" node="attribute" location="name" /> </field> </class> </mapping> | 現在,您應該很容易看懂這些代碼。這里指定了映射到的類(lèi)(Author)和這個(gè)類(lèi)上要映射的屬性(FirstName 和 LastName)。對于每個(gè)屬性,指定要查看的 XML 元素(都是 name)并指定需要的是屬性數據。 把 Book 和 Author 類(lèi)鏈接起來(lái) 如果看一下 上面 的 XML,就會(huì )注意到并沒(méi)有指定 Author 類(lèi)應該映射到哪個(gè) XML 元素。這是一個(gè)問(wèn)題,因為 Castor 不會(huì )猜測您的意圖;只要使用映射文件,最好指定所有映射信息。 如果每本書(shū)只有一位作者,那么在 Book 類(lèi)中可能有一個(gè) Author 屬性。在這種情況下,可以在映射文件中插入以下代碼: <?xml version="1.0"?> <!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN" "http://castor.org/mapping.dtd"> <mapping> <class name="ibm.xml.castor.Book"> <map-to xml="book" /> <field name="Title" type="java.lang.String"> <bind-xml name="title" node="element" location="book-info" /> </field> <field name="Isbn" type="java.lang.String"> <bind-xml name="isbn" node="element" location="book-info" /> </field> <field name="Author" type="ibm.xml.castor.Author"> <bind-xml name="author" /> </field> </class> <class name="ibm.xml.castor.Author"> <field name="FirstName" type="java.lang.String"> <bind-xml name="first" node="attribute" location="name" /> </field> <field name="LastName" type="java.lang.String"> <bind-xml name="last" node="attribute" location="name" /> </field> </class> </mapping> | 在這種情況下,把這一映射放在映射文件的圖書(shū)部分中,因為映射的是屬于 Book 類(lèi)的一個(gè)屬性。映射的類(lèi)型指定為 ibm.xml.castor.Author,并指定 XML 元素 author。這樣的話(huà),Castor 的映射系統就會(huì )使用 class 元素中的 Author 類(lèi)的定義處理作者的屬性。 但是,問(wèn)題在于 Book 類(lèi)中沒(méi)有 Author 屬性。相反,這個(gè)類(lèi)中有一個(gè) Authors 屬性,其中包含 Author 實(shí)例的集合。因此,必須讓 Castor 把每個(gè) author 元素映射到一個(gè) Author 實(shí)例(這一步差不多已經(jīng)完成了),然后把所有實(shí)例組合成 Book 的 Authors 屬性。
把元素映射到集合 為了映射圖書(shū)和作者的關(guān)系,需要把幾個(gè)元素(XML 文檔中的每個(gè) author)映射到一個(gè)集合,然后把這個(gè)集合分配給 Book 的 Authors 屬性。 首先使用 field 元素,因為確實(shí)要映射到 Book 的一個(gè)字段。還要把 name 屬性的值指定為 “Authors”,因為這是 Book 中將映射到的屬性: <field name="Authors"> </field> | 接下來(lái),需要提供屬性的類(lèi)型。您可能認為這應該是集合類(lèi)型。但是,實(shí)際上希望指定集合中每個(gè)成員的類(lèi)型。所以類(lèi)型應該是 ibm.xml.castor.Author。您將會(huì )獲得 ibm.xml.castor.Author 類(lèi)的實(shí)例,Castor 將把這些實(shí)例放到 Authors 屬性中: <field name="Authors" type="ibm.xml.castor.Author"> </field> | 下面是關(guān)鍵之處:使用 collection 屬性指定這個(gè)屬性是一個(gè)集合。這個(gè)屬性的值是集合的類(lèi)型。Castor 當前只支持兩個(gè)值:vector(代表列表類(lèi)型)和 array(代表數組類(lèi)型)。通過(guò)標準的 Java 集合 API(比如 next() 等調用)訪(fǎng)問(wèn)第一種集合;管理第二種集合的方法與 Java 數組相似,按照索引來(lái)訪(fǎng)問(wèn)它們,比如 ar[2]。在這個(gè)示例中,因為 Java 類(lèi)型是 List,所以使用 vector: <field name="Authors" type="ibm.xml.castor.Author" collection="vector"> </field> | 如果指定了 collection 屬性,Castor 就知道應該用與 type 屬性對應的值構建這個(gè)集合。因此,這里的 Authors 屬性應該是 ibm.xml.castor.Author 類(lèi)型的實(shí)例的集合。 現在只剩下一步了:指定獲取這些 Author 實(shí)例的來(lái)源。這要使用 bind-xml 元素: <field name="Authors" type="ibm.xml.castor.Author" collection="vector"> <bind-xml name="author" /> </field> | 所有工作都完成了;現在形成了一個(gè)完整的映射文件。最終的文件應該像清單 6 這樣。 清單 6. 完整的映射文件 <?xml version="1.0"?> <!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN" "http://castor.org/mapping.dtd"> <mapping> <class name="ibm.xml.castor.Book"> <map-to xml="book" /> <field name="Title" type="java.lang.String"> <bind-xml name="title" node="element" location="book-info" /> </field> <field name="Isbn" type="java.lang.String"> <bind-xml name="isbn" node="element" location="book-info" /> </field> <field name="Authors" type="ibm.xml.castor.Author" collection="vector"> <bind-xml name="author" /> </field> </class> <class name="ibm.xml.castor.Author"> <field name="FirstName" type="java.lang.String"> <bind-xml name="first" node="attribute" location="name" /> </field> <field name="LastName" type="java.lang.String"> <bind-xml name="last" node="attribute" location="name" /> </field> </class> </mapping> |
在程序中使用映射文件 最后,需要在解組過(guò)程中使用這個(gè)映射文件。以前,我們靜態(tài)地使用 Unmarshaller 類(lèi),通過(guò)調用 Unmarshaller.unmarshal() 把 XML 轉換為 Java 代碼。但是,因為現在要使用映射文件,所以需要創(chuàng )建一個(gè) Unmarshaller 實(shí)例并設置一些選項。清單 7 給出的類(lèi)處理從 XML 文檔到 Java 對象的解組過(guò)程。 清單 7. 用映射文件進(jìn)行解組 package ibm.xml.castor; import java.io.FileReader; import java.util.Iterator; import java.util.List; import org.exolab.castor.mapping.Mapping; import org.exolab.castor.xml.Unmarshaller; public class BookMapUnmarshaller { public static void main(String[] args) { Mapping mapping = new Mapping(); try { mapping.loadMapping("book-mapping.xml"); FileReader reader = new FileReader("book.xml"); Unmarshaller unmarshaller = new Unmarshaller(Book.class); unmarshaller.setMapping(mapping); Book book = (Book)unmarshaller.unmarshal(reader); System.out.println("Book ISBN: " + book.getIsbn()); System.out.println("Book Title: " + book.getTitle()); List authors = book.getAuthors(); for (Iterator i = authors.iterator(); i.hasNext(); ) { Author author = (Author)i.next(); System.out.println("Author: " + author.getFirstName() + " " + author.getLastName()); } } catch (Exception e) { System.err.println(e.getMessage()); e.printStackTrace(System.err); } } } | 與前兩篇文章中的解組器相比,這里的更改非常少。首先,創(chuàng )建一個(gè) Unmarshaller 實(shí)例,使用的參數是 Book.class。這告訴解組器要解組的頂級類(lèi)是哪個(gè)類(lèi)。注意,這個(gè)頂級 Java 類(lèi)對應于使用 map-to 元素的 mapping 元素。然后設置映射,最后調用 unmarshal() 的非靜態(tài)版本。 現在完成了!這個(gè)過(guò)程與以前的過(guò)程差異并不大。作為練習,您可以自己試著(zhù)編寫(xiě)把 Java 代碼編組為 XML 的代碼。請參考前一篇文章中的 BookMarshaller 類(lèi)并設置映射文件,然后嘗試在 XML 和 Java 代碼之間來(lái)回轉換。
結束語(yǔ) 數據綁定最終關(guān)注的是數據,而不是存儲數據的格式。對于大多數 Java 程序員來(lái)說(shuō),處理 Java 對象是很容易的,而通過(guò)數據綁定,能夠同樣輕松地把來(lái)自各種來(lái)源(尤其是 XML)的數據轉換為 Java 對象。另外,數據綁定環(huán)境中的映射甚至更進(jìn)了一步:在填充 Java 對象時(shí),可以非常靈活地處理數據源格式。因此,如果您喜歡數據綁定,那么一定也會(huì )喜歡映射;它使您能夠綁定那些與您需要的命名約定不太相符的 XML 文檔,也能夠使用與您的 Java 對象不相符的結構。 對于數據人員,映射會(huì )帶來(lái)同樣的好處。當調用 Java 方法并保存在命名古怪的 XML 風(fēng)格的變量中,或者 XML 文檔中有多個(gè)元素全部映射到同一個(gè)類(lèi),那么不需要構建中間層就可以從 Java 類(lèi)中取得所需的數據。最重要的是靈活性,能夠對數據做您 想做的事情,而不受框架或工具的限制。 后續內容 您已經(jīng)看到了 Castor 在 XML 環(huán)境中提供了什么。但是,這僅僅觸及到了 Castor 的皮毛。在下一篇文章中,將進(jìn)一步擴展簡(jiǎn)單的 XML 數據綁定并研究 Castor 的 SQL 數據綁定設施。我們將把數據從 Java 類(lèi)轉移到 SQL 數據庫中,再轉移回來(lái),而且不需要使用 JDBC。請復習一下 XML 和 SQL 知識,下個(gè)月我們將進(jìn)一步體驗數據綁定的威力。 學(xué)完下一篇文章(本系列的最后一篇)之后,您就能夠用相同的 API 在 XML、Java 和 SQL 數據庫之間進(jìn)行轉換。這甚至會(huì )帶來(lái)比映射文件更大的靈活性。對于所有數據存儲格式,可以使用單一 API 和相似的調用處理數據庫、Java 對象和 XML 文檔。實(shí)際上,對于那些了解 C# 的程序員來(lái)說(shuō),這聽(tīng)起來(lái)非常 像 LINQ(LINQ 是 Visual C# 2008 中最新最熱門(mén)的技術(shù)之一)。相似的功能已經(jīng)用 Java 技術(shù)實(shí)現了,而且具有一個(gè)穩定的 API。很棒,不是嗎?所以請繼續研究 Castor,綁定數據,試試您能實(shí)現哪些功能。享受創(chuàng )造的樂(lè )趣吧!我們網(wǎng)上相見(jiàn)。
下載 | 描述 | 名字 | 大小 | 下載方法 | | 文章示例代碼 | x-xjavacastor3-Code.zip | 46KB | HTTP |
參考資料 學(xué)習 獲得產(chǎn)品和技術(shù) 討論
關(guān)于作者 | | | | Brett McLaughlin 的著(zhù)作上過(guò)暢銷(xiāo)榜并獲得過(guò)非小說(shuō)類(lèi)圖書(shū)獎。他著(zhù)述豐富,包括計算機編程、家庭暴力、分析和設計,總印數超過(guò) 100,000 本。他編寫(xiě)、編輯和出版技術(shù)書(shū)籍快十年了,除了愜意地使用文字處理程序寫(xiě)書(shū)以外,他還喜歡彈奏吉他、和兩個(gè)兒子在屋子里追逐嬉鬧、和妻子觀(guān)看重新開(kāi)播的 Arrested Development。他的新著(zhù) Head First Object Oriented Analysis and Design 榮獲 2007 Jolt Technical Book 大獎。經(jīng)典著(zhù)作 Java and XML 仍然是關(guān)于在 Java 語(yǔ)言中使用 XML 技術(shù)的權威書(shū)籍。 |
|