?
對于主要關(guān)心文檔的數(shù)據(jù)內(nèi)容的應(yīng)用程序來說, Java 的 XML 數(shù)據(jù)綁定是 XML 文檔模型的強(qiáng)大替代方案。在本文中,企業(yè) Java 專家 Dennis Sosnoski 介紹了數(shù)據(jù)綁定并討論了什么使它如此吸引人。然后他向讀者展示了如何使用 Java 數(shù)據(jù)綁定的開放源代碼 Castor 框架處理日益復(fù)雜的文檔。如果您的應(yīng)用程序關(guān)心 XML 的數(shù)據(jù)更甚于關(guān)心 XML 文檔本身,您可能希望找出這個處理 Java 中 XML 的容易而又高效的方法。
?
?
大多數(shù)處理應(yīng)用程序中
XML
文檔的方法都是把重點(diǎn)放在
XML
上:從
XML
的角度處理文檔,并按照
XML
元素、屬性和字符數(shù)據(jù)內(nèi)容編程。如果您的應(yīng)用程序主要關(guān)心文檔的
XML
結(jié)構(gòu),這種方法是挺不錯的。對于關(guān)心文檔中包含的數(shù)據(jù)更甚于關(guān)心文檔本身的許多應(yīng)用程序來說,數(shù)據(jù)綁定(
data binding
)提供了一種簡單得多的處理
XML
的方法。
文檔模型與數(shù)據(jù)綁定
本系列中先前的文章所討論的文檔模型(請參閱參考資料)是數(shù)據(jù)綁定的最接近的替代方案。文檔模型和數(shù)據(jù)綁定都在內(nèi)存中構(gòu)建文檔表示,同時內(nèi)部表示和標(biāo)準(zhǔn)的文本
XML
之間可以互相轉(zhuǎn)換。兩者之間的不同是文檔模型盡可能接近地保存
XML
結(jié)構(gòu),而數(shù)據(jù)綁定只關(guān)心應(yīng)用程序使用的文檔數(shù)據(jù)。
為闡明這一點(diǎn),圖
1
顯示了一個簡單
XML
文檔的文檔模型視圖。文檔組件
?
本例中只有元素和文本節(jié)點(diǎn)
?
按照反映原始
XML
文檔的結(jié)構(gòu)鏈接在一起。將節(jié)點(diǎn)的結(jié)果樹和原始文檔關(guān)聯(lián)起來比較容易,但解釋樹中顯示的實(shí)際數(shù)據(jù)就不那么容易。
圖
1.
文檔的文檔模型視圖

??
如果您的應(yīng)用程序?yàn)?
XML
使用文檔模型方法,您就要處理這種類型的樹。在本例中,您將使用節(jié)點(diǎn)間的父-子關(guān)系縱向?yàn)g覽該樹,并使用公共父節(jié)點(diǎn)的多個子節(jié)點(diǎn)間的同級關(guān)系橫向?yàn)g覽該樹。您將能夠以非常詳細(xì)的級別操作樹結(jié)構(gòu),當(dāng)您把這棵樹序列化為文本時,生成的
XML
文檔將會反映您所做的更改(比如包含注釋)。
?
現(xiàn)在,把圖 1 和圖 2 對比一下,會顯示同一個文檔的數(shù)據(jù)綁定視圖。這里,原始 XML 文檔的結(jié)構(gòu)幾乎完全被轉(zhuǎn)換隱藏起來了,但通過一對對象,查看和訪問實(shí)際數(shù)據(jù)要容易得多。圖 2. 文檔的數(shù)據(jù)綁定視圖
?
使用這個數(shù)據(jù)結(jié)構(gòu)就是進(jìn)行正常的
Java
編程
?
您甚至不必知道關(guān)于
XML
的任何知識!(哦,我們在這里不要討論得太遠(yuǎn)了
?
我們的專家顧問還要吃飯呢
......
)參加您這個項(xiàng)目的人至少需要懂得如何建立數(shù)據(jù)結(jié)構(gòu)和
XML
文檔之間的映射,但這仍是簡化方向上的一大步。
數(shù)據(jù)綁定還可以提供其它的好處,并不是只簡化編程。由于它把許多文檔細(xì)節(jié)抽象出來,所以數(shù)據(jù)綁定所需的內(nèi)存通常少于文檔模型方法所需的內(nèi)存。例如,請考慮一下前面圖中顯示的兩個數(shù)據(jù)結(jié)構(gòu):文檔模型方法使用
10
個單獨(dú)的對象,而數(shù)據(jù)綁定使用兩個。由于要構(gòu)建的東西少得多,所以為文檔構(gòu)建數(shù)據(jù)綁定表示還可能更快。最后,使用數(shù)據(jù)綁定方法訪問程序內(nèi)的數(shù)據(jù)要比使用文檔模型快得多,因?yàn)槟梢钥刂迫绾伪硎竞痛鎯?shù)據(jù)。稍后我再回到這些問題。
如果數(shù)據(jù)綁定這么好,那么您何時想使用文檔模型代替它呢?在兩種情況下需要使用文檔模型:
您的應(yīng)用程序真正關(guān)心文檔結(jié)構(gòu)的細(xì)節(jié)。例如,如果您正在編寫一個
XML
文檔編輯器,您會希望一直使用文檔模型而不用數(shù)據(jù)綁定。
您正在處理的文檔不遵守固定的結(jié)構(gòu)。例如,數(shù)據(jù)綁定不會是實(shí)現(xiàn)一般
XML
文檔數(shù)據(jù)庫的好方法。
許多應(yīng)用程序使用
XML
進(jìn)行數(shù)據(jù)傳送,但在其它方面它們就不關(guān)心文檔表示的細(xì)節(jié)問題了。這些應(yīng)用程序是數(shù)據(jù)綁定的理想候選者。如果您的應(yīng)用程序適合這種模式,請繼續(xù)閱讀。
Castor
框架
目前,有許多不同的框架支持
Java
的
XML
數(shù)據(jù)綁定,但卻沒有一個標(biāo)準(zhǔn)接口。這一點(diǎn)最終會改變:
Java Community Process
(
JCP
)中的
JSR-031
正致力于定義一個標(biāo)準(zhǔn)(請參閱參考資料)。暫時我們先選擇一個框架學(xué)習(xí)使用它的接口。
在本文中,我選擇使用
Castor
數(shù)據(jù)綁定框架。
Castor
項(xiàng)目使用一個
BSD
類型的許可證,使得它可用于所有類型的應(yīng)用程序(包括完全歸私人所有的應(yīng)用程序)。通過支持
SQL
和
LDAP
綁定,
Castor
在
XML
數(shù)據(jù)綁定之外實(shí)際上也運(yùn)行得很好,盡管在本文中我將忽略這些其它的功能。從
2000
上半年就已經(jīng)開始對
Castor
進(jìn)行開發(fā)了,目前正處于高級
beta
測試版狀態(tài)(一般情況下可用,但如果需要錯誤修訂的話,您可能需要更新到當(dāng)前的
CVS
版本)。請參閱參考資料部分以獲得到
Castor
站點(diǎn)的鏈接,了解更多的詳細(xì)信息或者下載該軟件。
缺省綁定
開始使用
Castor
的
XML
數(shù)據(jù)綁定非常簡單。您甚至不需要定義一個
XML
文檔格式。只要您的數(shù)據(jù)出現(xiàn)在“類似
JavaBean
”(
JavaBean-like
)對象中,
Castor
就會生成一種文檔格式來自動表示數(shù)據(jù)并且稍后從那個文檔重新構(gòu)建原始數(shù)據(jù)。
數(shù)據(jù)綁定字典
下面是我在本文中使用的一些術(shù)語的一個微型字典:
組織(
Marshalling
)是為內(nèi)存中的對象生成
XML
表示的過程。與
Java
序列化一樣,這種表示需要包含所有依賴的對象:我們的主對象引用的對象、那些對象引用的對象,等等。
解組(
Unmarshalling
)是與組織相反的過程,在內(nèi)存中根據(jù)
XML
表示構(gòu)建一個對象(和依賴的對象)。
映射(
Mapping
)是用于組織和解組的一套規(guī)則。
Castor
有內(nèi)置的規(guī)則定義本文的這一部分所描述的缺省映射。它還允許您使用單獨(dú)的映射文件,在下面的部分您將看到這一點(diǎn)。
那么“類似
JavaBean
”是什么意思呢?真正的
JavaBeans
是一些可視組件,可以在開發(fā)環(huán)境中配置它們以便在
GUI
布局中使用。一些用真正的
JavaBeans
開始的慣例在
Java
社區(qū)中已經(jīng)變得非常普及,尤其是對于數(shù)據(jù)類。如果一個類遵守下列慣例,我就稱其為“類似
JavaBean
”:
該類是公共的
它定義了一個公共的缺省(無參數(shù))構(gòu)造函數(shù)
它定義了公共的
getX
和
setX
方法用來訪問屬性(數(shù)據(jù))值
既然技術(shù)上的定義不成問題,在談及這些“類似
JavaBean
”類的其中一個時,我將跳過所有這些,并只稱呼它為“
bean
”類。
我將使用航線航班時間表為例來講解貫穿本文的代碼,并從一個表示特定航班的簡單
bean
類開始來闡明它的工作機(jī)制。這個
bean
包含四個信息項(xiàng):
運(yùn)輸商(航空公司)標(biāo)識符
航班號
起飛時間
到達(dá)時間
下面的清單
1
顯示了用來處理航班信息的代碼。
清單
1.
航班信息
bean
public class FlightBean
{
private String m_carrier;
private int m_number;
private String m_departure;
private String m_arrival;
public FlightBean() {}
public void setCarrier(String carrier) {
m_carrier = carrier;
}
public String getCarrier() {
return m_carrier;
}
public void setNumber(int number) {
m_number = number;
}
public int getNumber() {
return m_number;
}
public void setDepartureTime(String time) {
m_departure = time;
}
public String getDepartureTime() {
return m_departure;
}
public void setArrivalTime(String time) {
m_arrival = time;
}
public String getArrivalTime() {
return m_arrival;
}
}
您可以看到,這個
bean
自身非常枯燥乏味,所以我想添加一個類并在缺省的
XML
綁定中使用這個類,如清單
2
所示。
清單
2.
缺省的數(shù)據(jù)綁定測試
import java.io.*;
import org.exolab.castor.xml.*;
public class Test
{
public static void main(String[] argv) {
// build a test bean
FlightBean bean = new FlightBean();
bean.setCarrier("AR");
bean.setNumber(426);
bean.setDepartureTime("6:23a");
bean.setArrivalTime("8:42a");
try {
// write it out as XML
File file = new File("test.xml");
Writer writer = new FileWriter(file);
Marshaller.marshal(bean, writer);
// now restore the value and list what we get
Reader reader = new FileReader(file);
FlightBean read = (FlightBean)
Unmarshaller.unmarshal(FlightBean.class, reader);
System.out.println("Flight " + read.getCarrier() +
read.getNumber() + " departing at " +
read.getDepartureTime() +
" and arriving at " + read.getArrivalTime());
} catch (IOException ex) {
ex.printStackTrace(System.err);
} catch (MarshalException ex) {
ex.printStackTrace(System.err);
} catch (ValidationException ex) {
ex.printStackTrace(System.err);
}
}
}
用
Castor
超越
bean
Castor
實(shí)際上不僅僅使用本文中所討論的“類似
JavaBean
”類。它還可以用公共成員變量訪問簡單數(shù)據(jù)對象類中的信息。例如,對
Test
類稍做更改就可以使用航班數(shù)據(jù)的下列定義,并仍然以相同的
XML
格式結(jié)束:
public class FlightData { public String carrier; public int number; public String departure; public String arrival; }
為使
Castor
正確地使用它,無論如何類必須一直存在。如果類定義了任何
getX
或
setX
方法,
Castor
就把它作為
bean
對待并只使用那些方法進(jìn)行組織和解組。
在這段代碼中,您首先構(gòu)建一個
FlightBean bean
并用一些封裝的值對它進(jìn)行初始化。然后您使用
Castor
的用于
bean
的缺省
XML
映射把
bean
寫到輸出文件。最后,使用同一個缺省的映射讀回生成的
XML
來重新構(gòu)建
bean
,然后從重新構(gòu)建的
bean
打印出信息。下面是結(jié)果:
Flight AR426 departing at 6:23a and arriving at 8:42a
這個輸出說明您已經(jīng)成功地對航班信息進(jìn)行了一次讀、寫來回(對于只用了兩次方法調(diào)用來說已經(jīng)不錯了)。現(xiàn)在,我將再深入一些,而不是只進(jìn)行控制臺輸出。
幕后情況
要更多地了解這個示例中發(fā)生了什么事,請看一下
Marshaller.marshal()
調(diào)用生成的
XML
。下面是這個文檔:
<?xml version="1.0"?>
<flight-bean number="426">
<arrival-time>8:42a</arrival-time>
<departure-time>6:23a</departure-time>
<carrier>AR</carrier>
</flight-bean>
Castor
使用
Java
內(nèi)省檢查您傳入
Marshaller.marshal()
調(diào)用的對象。在這個例子中,它找到了您定義的四個屬性值。
Castor
在輸出
XML
中創(chuàng)建一個元素(文檔的根元素)代表對象整體。在本例中,該元素的名稱源自對象類名,
flight-bean
。然后
Castor
用兩種方法的其中之一包含進(jìn)這個對象的屬性值。它創(chuàng)建:
每個基本數(shù)據(jù)類型值屬性作為元素的一個屬性(在這個例子中,只有
getNumber()
方法公開的
int
值
number
屬性)
每個對象值屬性作為根元素的子元素(這里是所有的其它子元素,因?yàn)樗鼈兪亲址?
結(jié)果是上面立即顯示的
XML
文檔。
更改
XML
格式
如果您不喜歡
Castor
的缺省映射格式,您可以很容易地更改映射。例如,在我們的航班信息示例中,假設(shè)我們需要數(shù)據(jù)的更緊湊的表示。使用屬性代替子元素會幫助實(shí)現(xiàn)這一點(diǎn),我們甚至可能希望使用比缺省值更短的名稱。與下面所示的文檔相似的文檔將非常適合我們的需要:
<?xml version="1.0"?>
<flight carrier="AR" depart="6:23a" arrive="8:42a" number="426"/>
定義映射
要讓
Castor
使用這種格式而不用缺省的格式,首先需要定義一個描述這種格式的映射。映射描述本身就是(非常驚訝吧)一個
XML
文檔。清單
3
顯示了把
bean
組織為以前顯示的格式的映射。
清單
3.
用來壓縮格式的映射
<!DOCTYPE databases PUBLIC
"-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
"http://castor.exolab.org/mapping.dtd">
<mapping>
<description>Basic mapping example</description>
<class name="FlightBean" auto-complete="true">
<map-to xml="flight"/>
<field name="carrier">
<bind-xml name="carrier" node="attribute"/>
</field>
<field name="departureTime">
<bind-xml name="depart" node="attribute"/>
</field>
<field name="arrivalTime">
<bind-xml name="arrive" node="attribute"/>
</field>
</class>
</mapping>
class
元素為已命名的類(在本例中是
FlightBean
)定義映射。您可以通過在這個元素中包含值為
true
的可選
auto-complete
屬性告訴
Castor
為元素主體內(nèi)沒有明確列出的類的任何屬性都使用缺省的映射。這一點(diǎn)在這里很方便,因?yàn)樵缫呀?jīng)按您希望的方式處理過
number
屬性了。
子
map-to
元素告訴
Castor
把
FlightBean
類的實(shí)例映射為
XML
文檔中的
flight
元素。如果您繼續(xù)使用在幕后情況中的缺省映射輸出示例中看到的
flight-bean
的缺省元素名,那么這個元素就是可選的。
最后,您可以為您想用不同于缺省屬性的處理方法處理的每個屬性包含一個子
field
元素。所有這些都遵守同一個模式:
name
屬性給出屬性名,子
bind-xml
元素告訴
Castor
如何映射該屬性。在本例中,您告訴它把每個屬性都映射為帶有給定名稱的屬性。
使用映射
既然已經(jīng)有了定義好的映射,那么您就需要告訴
Castor
框架在組織和解組數(shù)據(jù)時使用該映射。清單
4
顯示為實(shí)現(xiàn)這一點(diǎn)需要對先前的代碼做的更改。
清單
4.
用映射進(jìn)行組織和解組
...
// write it out as XML (if not already present)
Mapping map = new Mapping();
map.loadMapping("mapping.xml");
File file = new File("test.xml");
Writer writer = new FileWriter(file);
Marshaller marshaller = new Marshaller(writer);
marshaller.setMapping(map);
marshaller.marshal(bean);
// now restore the value and list what we get
Reader reader = new FileReader(file);
Unmarshaller unmarshaller = new Unmarshaller(map);
FlightBean read = (FlightBean)unmarshaller.unmarshal(reader);
...
} catch (MappingException ex) {
ex.printStackTrace(System.err);
...
這段代碼比先前清單
2
中所示的使用缺省映射的代碼復(fù)雜了一點(diǎn)。在進(jìn)行任何其它操作之前,請先創(chuàng)建一個
Mapping
對象并加載您的映射定義。實(shí)際的組織和解組也是不一樣的。要使用映射,需要創(chuàng)建
Marshaller
和
Unmarshaller
對象,用映射配置它們,并調(diào)用這些對象的方法而不是調(diào)用第一個示例中的靜態(tài)方法。最后,必須處理映射錯誤生成的另一種異常類型。
完成這些更改后,您可以再次嘗試運(yùn)行這個測試程序。控制臺輸出與第一個示例一樣(如清單
2
所示),但現(xiàn)在
XML
文檔看起來正如您希望的那樣:
<?xml version="1.0"?>
<flight carrier="AR" depart="6:23a" arrive="8:42a" number="426"/>
處理集合
既然單個的航班數(shù)據(jù)是用的您喜歡的格式,那么您可以定義更高級的結(jié)構(gòu):路線數(shù)據(jù)。這個結(jié)構(gòu)將包含往返機(jī)場的標(biāo)識符以及那條路線上的一個航班集合。清單
5
顯示了擁有這些信息的
bean
類的一個示例。
清單
5.
路線信息
bean
import java.util.ArrayList;
public class RouteBean
{
private String m_from;
private String m_to;
private ArrayList m_flights;
public RouteBean() {
m_flights = new ArrayList();
}
public void setFrom(String from) {
m_from = from;
}
public String getFrom() {
return m_from;
}
public void setTo(String to) {
m_to = to;
}
public String getTo() {
return m_to;
}
public ArrayList getFlights() {
return m_flights;
}
public void addFlight(FlightBean flight) {
m_flights.add(flight);
}
}
在這段代碼中,我已經(jīng)定義了一個
addFlight()
方法用來在路線上一次一個設(shè)置航班。這是在測試程序中構(gòu)建數(shù)據(jù)結(jié)構(gòu)的一種很方便的方法,但與您預(yù)料的相反,
Castor
在解組時并不使用這種方法向路線添加航班。相反,它使用
getFlights()
方法訪問航班集合,然后直接向該集合添加航班。
在映射中處理航班集合只需使用上個示例(如清單
3
所示)中使用的
field
元素的變體。清單
6
顯示了修改過的映射文件。
清單
6.
用于帶航班集合的路線的映射
<!DOCTYPE databases PUBLIC
"-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
"http://castor.exolab.org/mapping.dtd">
<mapping>
<description>Collection mapping example</description>
<class name="RouteBean">
<map-to xml="route"/>
<field name="from">
<bind-xml name="from" node="attribute"/>
</field>
<field name="to">
<bind-xml name="to" node="attribute"/>
</field>
<field name="flights" collection="collection" type="FlightBean">
<bind-xml name="flight"/>
</field>
</class>
<class name="FlightBean" auto-complete="true">
<field name="carrier">
<bind-xml name="carrier" node="attribute"/>
</field>
<field name="departureTime">
<bind-xml name="depart" node="attribute"/>
</field>
<field name="arrivalTime">
<bind-xml name="arrive" node="attribute"/>
</field>
</class>
</mapping>
除定義
RouteBean
的
flights
屬性的
field
元素之外,一切都與上一個映射(如清單
3
所示)完全相同。這個映射使用前面不需要的一對屬性。帶有值
collection
的
collection
屬性將這個屬性定義為一個
java.util.Collection
(其它值定義數(shù)組、
java.util.Vectors
等)。
type
屬性定義包含在集合中的對象的類型,用全限定類名作為值。這里的值就是
FlightBean
,因?yàn)槲覜]有使用類包。
其它的不同是我不再需要使用
FlightBean
類元素內(nèi)的
map-to
子元素為綁定定義元素名。定義
RouteBean
的
flights
屬性的
field
元素通過它的子
bind-xml
元素做這項(xiàng)工作。既然組織和解組
FlightBean
對象的唯一方法是通過這個屬性,它們將一直使用這個
bind-xml
元素設(shè)置的名稱。
我不想找麻煩去顯示這個示例的測試程序,因?yàn)樗臄?shù)據(jù)綁定部分與上個示例的相同。下面是為一些樣本數(shù)據(jù)生成的
XML
文檔的樣子:
<?xml version="1.0"?>
<route from="SEA" to="LAX">
<flight carrier="AR" depart="6:23a" arrive="8:42a"
number="426"/>
<flight carrier="CA" depart="8:10a" arrive="10:52a"
number="833"/>
<flight carrier="AR" depart="9:00a" arrive="11:36a"
number="433"/>
</route>
對象引用
現(xiàn)在,您終于準(zhǔn)備好要處理整個航班時間表。為此,您將向集合中再添加三個
bean
:
AirportBean
用來提供機(jī)場信息
CarrierBean
用來提供航線信息
TimeTableBean
用來滿足一切要求
為使它比較有趣,除上個示例(在處理集合中顯示)中使用的
RouteBean
和
FlightBean
之間的所有權(quán)關(guān)系之外,您還要添加一些
bean
間的鏈接。
鏈接
bean
對于第一個添加的關(guān)系,將
FlightBean
更改為直接引用運(yùn)輸商信息而不是只使用代碼標(biāo)識運(yùn)輸商。下面是
FlightBean
的變化:
public class FlightBean
{
private CarrierBean m_carrier;
...
public void setCarrier(CarrierBean carrier) {
m_carrier = carrier;
}
public CarrierBean getCarrier() {
return m_carrier;
}
...
}
現(xiàn)在,為
RouteBean
做同樣的工作使其引用機(jī)場信息:
public class RouteBean
{
private AirportBean m_from;
private AirportBean m_to;
...
public void setFrom(AirportBean from) {
m_from = from;
}
public AirportBean getFrom() {
return m_from;
}
public void setTo(AirportBean to) {
m_to = to;
}
public AirportBean getTo() {
return m_to;
}
...
}
我不會引入被添加的
bean
本身的代碼,因?yàn)樗鼈冿@示的內(nèi)容前面都已經(jīng)做過了。您可以下載
code.jar
下載文件中所有示例的完整的代碼(請參閱參考資料)。
映射引用
您將需要使用映射文檔的其它一些功能來支持您正在組織和解組的對象間的引用。清單
7
顯示了完整的映射:
清單
7.
整個時間表的映射
<!DOCTYPE databases PUBLIC
"-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
"http://castor.exolab.org/mapping.dtd">
<mapping>
<description>Reference mapping example</description>
<class name="TimeTableBean">
<map-to xml="timetable"/>
<field name="carriers" type="CarrierBean" collection="collection">
<bind-xml name="carrier"/>
</field>
<field name="airports" type="AirportBean" collection="collection">
<bind-xml name="airport"/>
</field>
<field name="routes" type="RouteBean" collection="collection">
<bind-xml name="route"/>
</field>
</class>
<class name="CarrierBean" identity="ident" auto-complete="true">
<field name="ident">
<bind-xml name="ident" node="attribute"/>
</field>
</class>
<class name="AirportBean" identity="ident" auto-complete="true">
<field name="ident">
<bind-xml name="ident" node="attribute"/>
</field>
</class>
<class name="RouteBean">
<field name="from" type="AirportBean">
<bind-xml name="from" node="attribute" reference="true"/>
</field>
<field name="to" type="AirportBean">
<bind-xml name="to" node="attribute" reference="true"/>
</field>
<field name="flights" type="FlightBean" collection="collection">
<bind-xml name="flight"/>
</field>
</class>
<class name="FlightBean" auto-complete="true">
<field name="carrier">
<bind-xml name="carrier" node="attribute" reference="true"/>
</field>
<field name="departureTime">
<bind-xml name="depart" node="attribute"/>
</field>
<field name="arrivalTime">
<bind-xml name="arrive" node="attribute"/>
</field>
</class>
</mapping>
除被添加的
bean
之外,這里重要的改變是添加了
identity
和
reference
屬性。
class
元素的
identity
屬性告訴
Castor
已命名的屬性是該類的一個實(shí)例的唯一標(biāo)識符。這里,我讓
CarrierBean
和
AirportBean
都把它們的
ident
屬性定義為標(biāo)識符。
bind-xml
元素的
reference
屬性提供
Castor
進(jìn)行映射所需的鏈接信息的另一部分。
reference
設(shè)置為
true
的映射告訴
Castor
為引用的對象組織標(biāo)識符,而不要對象自身的副本。我已經(jīng)為路線兩端從
RouteBean
到鏈接的
AirportBean
的引用,以及從
FlightBean
到鏈接的
CarrierBean
的引用使用了這種技術(shù)。
當(dāng)
Castor
使用這種類型的映射解組數(shù)據(jù)時,它自動把對象標(biāo)識符轉(zhuǎn)換為對實(shí)際對象的引用。您需要確保標(biāo)識符的值是真正唯一的,即便是不同類型對象的標(biāo)識符也不能重復(fù)。對于這個示例中的數(shù)據(jù),這不是問題:運(yùn)輸商標(biāo)識符是兩個字符,機(jī)場標(biāo)識符是三個字符,所以它們永遠(yuǎn)也不會相同。當(dāng)確實(shí)有可能沖突的情況發(fā)生時,您可以輕易地避開這個問題,只要為每個標(biāo)識符添加前綴,這個前綴是代表標(biāo)識符所表示的對象類型的唯一代碼。
已組織的時間表
除設(shè)置了更多樣本數(shù)據(jù)外,這個示例在測試代碼中并沒有包含什么新內(nèi)容。清單
8
顯示了組織操作生成的
XML
文檔:
清單
8.
已組織的時間表
<?xml version="1.0"?>
<timetable>
<carrier ident="AR" rating="9">
<URL>;http://www.arcticairlines.com<;/URL>
<name>Arctic Airlines</name>
</carrier>
<carrier ident="CA" rating="7">
<URL>;http://www.combinedlines.com<;/URL>
<name>Combined Airlines</name>
</carrier>
<airport ident="SEA">
<location>Seattle, WA</location>
<name>Seattle-Tacoma International Airport</name>
</airport>
<airport ident="LAX">
<location>Los Angeles, CA</location>
<name>Los Angeles International Airport</name>
</airport>
<route from="SEA" to="LAX">
<flight carrier="AR" depart="6:23a" arrive="8:42a" number="426"/>
<flight carrier="CA" depart="8:10a" arrive="10:52a" number="833"/>
<flight carrier="AR" depart="9:00a" arrive="11:36a" number="433"/>
</route>
<route from="LAX" to="SEA">
<flight carrier="CA" depart="7:45a" arrive="10:20a" number="311"/>
<flight carrier="AR" depart="9:27a" arrive="12:04p" number="593"/>
<flight carrier="AR" depart="12:30p" arrive="3:07p" number="102"/>
</route>
</timetable>
處理數(shù)據(jù)
既然已經(jīng)最終設(shè)置了時間表的所有數(shù)據(jù),我們來快速看一下如何在程序中操作這些數(shù)據(jù)。使用數(shù)據(jù)綁定,您已經(jīng)為時間表構(gòu)建了一個數(shù)據(jù)結(jié)構(gòu),該時間表由幾種類型的
bean
組成。處理這些數(shù)據(jù)的應(yīng)用程序代碼可以直接使用這些
bean
。
例如,假設(shè)您想查找西雅圖和洛杉磯之間的往返旅程選擇,但只限于被指定為最低等級的運(yùn)輸商。清單
9
顯示了使用數(shù)據(jù)綁定
bean
結(jié)構(gòu)獲取這些信息的基本代碼(關(guān)于完整的詳細(xì)信息,請參閱參考資料中的源代碼下載)。
清單
9.
航班查找程序代碼
private static void listFlights(TimeTableBean top, String from,
String to, int rating) {
// find the routes for outbound and inbound flights
Iterator r_iter = top.getRoutes().iterator();
RouteBean in = null;
RouteBean out = null;
while (r_iter.hasNext()) {
RouteBean route = (RouteBean)r_iter.next();
if (route.getFrom().getIdent().equals(from) &&
route.getTo().getIdent().equals(to)) {
out = route;
} else if (route.getFrom().getIdent().equals(to) &&
route.getTo().getIdent().equals(from)) {
in = route;
}
}
// make sure we found the routes
if (in != null && out != null) {
// find outbound flights meeting carrier rating requirement
Iterator o_iter = out.getFlights().iterator();
while (o_iter.hasNext()) {
FlightBean o_flight = (FlightBean)o_iter.next();
if (o_flight.getCarrier().getRating() >= rating) {
// find inbound flights meeting carrier rating
// requirement, and leaving after outbound arrives
int time = timeToMinute(o_flight.getArrivalTime());
Iterator i_iter = in.getFlights().iterator();
while (i_iter.hasNext()) {
FlightBean i_flight = (FlightBean)i_iter.next();
if (i_flight.getCarrier().getRating() >= rating
&&
timeToMinute(i_flight.getDepartureTime())
> time) {
// list the flight combination
printFlights(o_flight, i_flight, from, to);
}
}
}
}
}
}
您可以使用清單
8
中早就顯示的樣本數(shù)據(jù)進(jìn)行試驗(yàn)。如果您查詢從西雅圖(
SEA
)到洛杉磯(
LAX
),運(yùn)輸商級別等于或高于
8
的航班,結(jié)果如下:
Leave SEA on Arctic Airlines 426 at 6:23a
return from LAX on Arctic Airlines 593 at 9:27a
Leave SEA on Arctic Airlines 426 at 6:23a
return from LAX on Arctic Airlines 102 at 12:30p
Leave SEA on Arctic Airlines 433 at 9:00a
return from LAX on Arctic Airlines 102 at 12:30p
文檔模型比較
在這里我不打算嘗試徹底分析使用其中一種
XML
文檔模型的等價代碼;那樣的話太復(fù)雜,簡直要再單獨(dú)寫一篇文章。解決這個問題的最簡單方法可能是首先解析
carrier
元素,然后建立一個把每個標(biāo)識符代碼鏈接到相應(yīng)元素的映射。然后,使用與清單
9
中的示例代碼相似的邏輯。每一步都比
bean
示例更復(fù)雜,因?yàn)榇a處理的是
XML
組件而不是實(shí)際的數(shù)據(jù)值。性能可能也要差得多
?
如果您只是對數(shù)據(jù)執(zhí)行幾個操作就沒問題,但如果它位于應(yīng)用程序的核心處,就是大問題了。
如果在
bean
和
XML
之間的映射中使用更多的數(shù)據(jù)類型轉(zhuǎn)換,差異(在代碼復(fù)雜性和性能方面)會更大。例如,如果大量處理航班時間,您可能希望把文本時間轉(zhuǎn)換為更好的內(nèi)部表示(比如一天內(nèi)的分鐘數(shù),如清單
9
所示)。您可以為文本相對內(nèi)部格式(把映射設(shè)置為只使用文本格式)定義另外的
get
和
set
方法,或者定義一個定制的
org.exolab.castor.mapping.FieldHandler
實(shí)現(xiàn)供
Castor
把該實(shí)現(xiàn)與這些值一起使用。保持時間值為內(nèi)部格式允許您在設(shè)法匹配清單
9
中的航班時跳過轉(zhuǎn)換,從而使處理速度更快。
Castor
提供了許多用于定制的
hook
,但在本文的討論范圍之外:特殊的
FieldHandler
只是一個示例。理想情況下,樣本代碼和討論應(yīng)該已經(jīng)使您感覺到該框架的功能和靈活性。我鼓勵您自己進(jìn)一步試驗(yàn)
Castor
。我想您會像我一樣發(fā)現(xiàn)
Castor
那樣有用(那樣有趣)。
結(jié)束語
數(shù)據(jù)綁定是使用
XML
進(jìn)行數(shù)據(jù)交換的應(yīng)用程序中的文檔模型的一個非常不錯的替代方案。它簡化了您的編程,因?yàn)槟恍枰侔凑?
XML
去考慮問題。相反,您可以直接處理表示應(yīng)用程序使用的數(shù)據(jù)的意義的對象。它還提供使內(nèi)存和處理器性能比使用文檔模型時更好的潛力。
在本文中,我已經(jīng)討論了一系列越來越復(fù)雜的、使用
Castor
框架的數(shù)據(jù)綁定示例。所有這些示例都使用我所謂的直接(
direct
)數(shù)據(jù)綁定:開發(fā)者根據(jù)數(shù)據(jù)定義類,然后將數(shù)據(jù)映射為
XML
文檔結(jié)構(gòu)。在下一篇文章中,我將探討另一種方法:模式(
schema
)數(shù)據(jù)綁定,它使用文檔模式(比如
DTD
、
XML Schema
或另一種形式)并生成與該模式相應(yīng)的代碼。
Castor
同時支持模式方法和您在本文中看到的直接綁定,所以在后面的文章中您會看到
Castor
的更多信息。我還將看一下致力于
Java
數(shù)據(jù)綁定(
Java Data Binding
)標(biāo)準(zhǔn)的
JSR-031
的進(jìn)展并比較這兩種方法的性能。請留心這個領(lǐng)域了解更多關(guān)于
Java
中
XML
數(shù)據(jù)綁定的信息,不久它就會出現(xiàn)在您身邊的
IBM developerWorks
上
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
