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

??
如果您的應用程序為
XML
使用文檔模型方法,您就要處理這種類型的樹。在本例中,您將使用節點間的父-子關系縱向瀏覽該樹,并使用公共父節點的多個子節點間的同級關系橫向瀏覽該樹。您將能夠以非常詳細的級別操作樹結構,當您把這棵樹序列化為文本時,生成的
XML
文檔將會反映您所做的更改(比如包含注釋)。
?
現在,把圖 1 和圖 2 對比一下,會顯示同一個文檔的數據綁定視圖。這里,原始 XML 文檔的結構幾乎完全被轉換隱藏起來了,但通過一對對象,查看和訪問實際數據要容易得多。圖 2. 文檔的數據綁定視圖
?
使用這個數據結構就是進行正常的
Java
編程
?
您甚至不必知道關于
XML
的任何知識!(哦,我們在這里不要討論得太遠了
?
我們的專家顧問還要吃飯呢
......
)參加您這個項目的人至少需要懂得如何建立數據結構和
XML
文檔之間的映射,但這仍是簡化方向上的一大步。
數據綁定還可以提供其它的好處,并不是只簡化編程。由于它把許多文檔細節抽象出來,所以數據綁定所需的內存通常少于文檔模型方法所需的內存。例如,請考慮一下前面圖中顯示的兩個數據結構:文檔模型方法使用
10
個單獨的對象,而數據綁定使用兩個。由于要構建的東西少得多,所以為文檔構建數據綁定表示還可能更快。最后,使用數據綁定方法訪問程序內的數據要比使用文檔模型快得多,因為您可以控制如何表示和存儲數據。稍后我再回到這些問題。
如果數據綁定這么好,那么您何時想使用文檔模型代替它呢?在兩種情況下需要使用文檔模型:
您的應用程序真正關心文檔結構的細節。例如,如果您正在編寫一個
XML
文檔編輯器,您會希望一直使用文檔模型而不用數據綁定。
您正在處理的文檔不遵守固定的結構。例如,數據綁定不會是實現一般
XML
文檔數據庫的好方法。
許多應用程序使用
XML
進行數據傳送,但在其它方面它們就不關心文檔表示的細節問題了。這些應用程序是數據綁定的理想候選者。如果您的應用程序適合這種模式,請繼續閱讀。
Castor
框架
目前,有許多不同的框架支持
Java
的
XML
數據綁定,但卻沒有一個標準接口。這一點最終會改變:
Java Community Process
(
JCP
)中的
JSR-031
正致力于定義一個標準(請參閱參考資料)。暫時我們先選擇一個框架學習使用它的接口。
在本文中,我選擇使用
Castor
數據綁定框架。
Castor
項目使用一個
BSD
類型的許可證,使得它可用于所有類型的應用程序(包括完全歸私人所有的應用程序)。通過支持
SQL
和
LDAP
綁定,
Castor
在
XML
數據綁定之外實際上也運行得很好,盡管在本文中我將忽略這些其它的功能。從
2000
上半年就已經開始對
Castor
進行開發了,目前正處于高級
beta
測試版狀態(一般情況下可用,但如果需要錯誤修訂的話,您可能需要更新到當前的
CVS
版本)。請參閱參考資料部分以獲得到
Castor
站點的鏈接,了解更多的詳細信息或者下載該軟件。
缺省綁定
開始使用
Castor
的
XML
數據綁定非常簡單。您甚至不需要定義一個
XML
文檔格式。只要您的數據出現在“類似
JavaBean
”(
JavaBean-like
)對象中,
Castor
就會生成一種文檔格式來自動表示數據并且稍后從那個文檔重新構建原始數據。
數據綁定字典
下面是我在本文中使用的一些術語的一個微型字典:
組織(
Marshalling
)是為內存中的對象生成
XML
表示的過程。與
Java
序列化一樣,這種表示需要包含所有依賴的對象:我們的主對象引用的對象、那些對象引用的對象,等等。
解組(
Unmarshalling
)是與組織相反的過程,在內存中根據
XML
表示構建一個對象(和依賴的對象)。
映射(
Mapping
)是用于組織和解組的一套規則。
Castor
有內置的規則定義本文的這一部分所描述的缺省映射。它還允許您使用單獨的映射文件,在下面的部分您將看到這一點。
那么“類似
JavaBean
”是什么意思呢?真正的
JavaBeans
是一些可視組件,可以在開發環境中配置它們以便在
GUI
布局中使用。一些用真正的
JavaBeans
開始的慣例在
Java
社區中已經變得非常普及,尤其是對于數據類。如果一個類遵守下列慣例,我就稱其為“類似
JavaBean
”:
該類是公共的
它定義了一個公共的缺省(無參數)構造函數
它定義了公共的
getX
和
setX
方法用來訪問屬性(數據)值
既然技術上的定義不成問題,在談及這些“類似
JavaBean
”類的其中一個時,我將跳過所有這些,并只稱呼它為“
bean
”類。
我將使用航線航班時間表為例來講解貫穿本文的代碼,并從一個表示特定航班的簡單
bean
類開始來闡明它的工作機制。這個
bean
包含四個信息項:
運輸商(航空公司)標識符
航班號
起飛時間
到達時間
下面的清單
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.
缺省的數據綁定測試
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
實際上不僅僅使用本文中所討論的“類似
JavaBean
”類。它還可以用公共成員變量訪問簡單數據對象類中的信息。例如,對
Test
類稍做更改就可以使用航班數據的下列定義,并仍然以相同的
XML
格式結束:
public class FlightData { public String carrier; public int number; public String departure; public String arrival; }
為使
Castor
正確地使用它,無論如何類必須一直存在。如果類定義了任何
getX
或
setX
方法,
Castor
就把它作為
bean
對待并只使用那些方法進行組織和解組。
在這段代碼中,您首先構建一個
FlightBean bean
并用一些封裝的值對它進行初始化。然后您使用
Castor
的用于
bean
的缺省
XML
映射把
bean
寫到輸出文件。最后,使用同一個缺省的映射讀回生成的
XML
來重新構建
bean
,然后從重新構建的
bean
打印出信息。下面是結果:
Flight AR426 departing at 6:23a and arriving at 8:42a
這個輸出說明您已經成功地對航班信息進行了一次讀、寫來回(對于只用了兩次方法調用來說已經不錯了)。現在,我將再深入一些,而不是只進行控制臺輸出。
幕后情況
要更多地了解這個示例中發生了什么事,請看一下
Marshaller.marshal()
調用生成的
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
內省檢查您傳入
Marshaller.marshal()
調用的對象。在這個例子中,它找到了您定義的四個屬性值。
Castor
在輸出
XML
中創建一個元素(文檔的根元素)代表對象整體。在本例中,該元素的名稱源自對象類名,
flight-bean
。然后
Castor
用兩種方法的其中之一包含進這個對象的屬性值。它創建:
每個基本數據類型值屬性作為元素的一個屬性(在這個例子中,只有
getNumber()
方法公開的
int
值
number
屬性)
每個對象值屬性作為根元素的子元素(這里是所有的其它子元素,因為它們是字符串)。
結果是上面立即顯示的
XML
文檔。
更改
XML
格式
如果您不喜歡
Castor
的缺省映射格式,您可以很容易地更改映射。例如,在我們的航班信息示例中,假設我們需要數據的更緊湊的表示。使用屬性代替子元素會幫助實現這一點,我們甚至可能希望使用比缺省值更短的名稱。與下面所示的文檔相似的文檔將非常適合我們的需要:
<?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
為元素主體內沒有明確列出的類的任何屬性都使用缺省的映射。這一點在這里很方便,因為早已經按您希望的方式處理過
number
屬性了。
子
map-to
元素告訴
Castor
把
FlightBean
類的實例映射為
XML
文檔中的
flight
元素。如果您繼續使用在幕后情況中的缺省映射輸出示例中看到的
flight-bean
的缺省元素名,那么這個元素就是可選的。
最后,您可以為您想用不同于缺省屬性的處理方法處理的每個屬性包含一個子
field
元素。所有這些都遵守同一個模式:
name
屬性給出屬性名,子
bind-xml
元素告訴
Castor
如何映射該屬性。在本例中,您告訴它把每個屬性都映射為帶有給定名稱的屬性。
使用映射
既然已經有了定義好的映射,那么您就需要告訴
Castor
框架在組織和解組數據時使用該映射。清單
4
顯示為實現這一點需要對先前的代碼做的更改。
清單
4.
用映射進行組織和解組
...
// 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
中所示的使用缺省映射的代碼復雜了一點。在進行任何其它操作之前,請先創建一個
Mapping
對象并加載您的映射定義。實際的組織和解組也是不一樣的。要使用映射,需要創建
Marshaller
和
Unmarshaller
對象,用映射配置它們,并調用這些對象的方法而不是調用第一個示例中的靜態方法。最后,必須處理映射錯誤生成的另一種異常類型。
完成這些更改后,您可以再次嘗試運行這個測試程序。控制臺輸出與第一個示例一樣(如清單
2
所示),但現在
XML
文檔看起來正如您希望的那樣:
<?xml version="1.0"?>
<flight carrier="AR" depart="6:23a" arrive="8:42a" number="426"/>
處理集合
既然單個的航班數據是用的您喜歡的格式,那么您可以定義更高級的結構:路線數據。這個結構將包含往返機場的標識符以及那條路線上的一個航班集合。清單
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);
}
}
在這段代碼中,我已經定義了一個
addFlight()
方法用來在路線上一次一個設置航班。這是在測試程序中構建數據結構的一種很方便的方法,但與您預料的相反,
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
(其它值定義數組、
java.util.Vectors
等)。
type
屬性定義包含在集合中的對象的類型,用全限定類名作為值。這里的值就是
FlightBean
,因為我沒有使用類包。
其它的不同是我不再需要使用
FlightBean
類元素內的
map-to
子元素為綁定定義元素名。定義
RouteBean
的
flights
屬性的
field
元素通過它的子
bind-xml
元素做這項工作。既然組織和解組
FlightBean
對象的唯一方法是通過這個屬性,它們將一直使用這個
bind-xml
元素設置的名稱。
我不想找麻煩去顯示這個示例的測試程序,因為它的數據綁定部分與上個示例的相同。下面是為一些樣本數據生成的
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>
對象引用
現在,您終于準備好要處理整個航班時間表。為此,您將向集合中再添加三個
bean
:
AirportBean
用來提供機場信息
CarrierBean
用來提供航線信息
TimeTableBean
用來滿足一切要求
為使它比較有趣,除上個示例(在處理集合中顯示)中使用的
RouteBean
和
FlightBean
之間的所有權關系之外,您還要添加一些
bean
間的鏈接。
鏈接
bean
對于第一個添加的關系,將
FlightBean
更改為直接引用運輸商信息而不是只使用代碼標識運輸商。下面是
FlightBean
的變化:
public class FlightBean
{
private CarrierBean m_carrier;
...
public void setCarrier(CarrierBean carrier) {
m_carrier = carrier;
}
public CarrierBean getCarrier() {
return m_carrier;
}
...
}
現在,為
RouteBean
做同樣的工作使其引用機場信息:
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
本身的代碼,因為它們顯示的內容前面都已經做過了。您可以下載
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
已命名的屬性是該類的一個實例的唯一標識符。這里,我讓
CarrierBean
和
AirportBean
都把它們的
ident
屬性定義為標識符。
bind-xml
元素的
reference
屬性提供
Castor
進行映射所需的鏈接信息的另一部分。
reference
設置為
true
的映射告訴
Castor
為引用的對象組織標識符,而不要對象自身的副本。我已經為路線兩端從
RouteBean
到鏈接的
AirportBean
的引用,以及從
FlightBean
到鏈接的
CarrierBean
的引用使用了這種技術。
當
Castor
使用這種類型的映射解組數據時,它自動把對象標識符轉換為對實際對象的引用。您需要確保標識符的值是真正唯一的,即便是不同類型對象的標識符也不能重復。對于這個示例中的數據,這不是問題:運輸商標識符是兩個字符,機場標識符是三個字符,所以它們永遠也不會相同。當確實有可能沖突的情況發生時,您可以輕易地避開這個問題,只要為每個標識符添加前綴,這個前綴是代表標識符所表示的對象類型的唯一代碼。
已組織的時間表
除設置了更多樣本數據外,這個示例在測試代碼中并沒有包含什么新內容。清單
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>
處理數據
既然已經最終設置了時間表的所有數據,我們來快速看一下如何在程序中操作這些數據。使用數據綁定,您已經為時間表構建了一個數據結構,該時間表由幾種類型的
bean
組成。處理這些數據的應用程序代碼可以直接使用這些
bean
。
例如,假設您想查找西雅圖和洛杉磯之間的往返旅程選擇,但只限于被指定為最低等級的運輸商。清單
9
顯示了使用數據綁定
bean
結構獲取這些信息的基本代碼(關于完整的詳細信息,請參閱參考資料中的源代碼下載)。
清單
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
中早就顯示的樣本數據進行試驗。如果您查詢從西雅圖(
SEA
)到洛杉磯(
LAX
),運輸商級別等于或高于
8
的航班,結果如下:
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
文檔模型的等價代碼;那樣的話太復雜,簡直要再單獨寫一篇文章。解決這個問題的最簡單方法可能是首先解析
carrier
元素,然后建立一個把每個標識符代碼鏈接到相應元素的映射。然后,使用與清單
9
中的示例代碼相似的邏輯。每一步都比
bean
示例更復雜,因為代碼處理的是
XML
組件而不是實際的數據值。性能可能也要差得多
?
如果您只是對數據執行幾個操作就沒問題,但如果它位于應用程序的核心處,就是大問題了。
如果在
bean
和
XML
之間的映射中使用更多的數據類型轉換,差異(在代碼復雜性和性能方面)會更大。例如,如果大量處理航班時間,您可能希望把文本時間轉換為更好的內部表示(比如一天內的分鐘數,如清單
9
所示)。您可以為文本相對內部格式(把映射設置為只使用文本格式)定義另外的
get
和
set
方法,或者定義一個定制的
org.exolab.castor.mapping.FieldHandler
實現供
Castor
把該實現與這些值一起使用。保持時間值為內部格式允許您在設法匹配清單
9
中的航班時跳過轉換,從而使處理速度更快。
Castor
提供了許多用于定制的
hook
,但在本文的討論范圍之外:特殊的
FieldHandler
只是一個示例。理想情況下,樣本代碼和討論應該已經使您感覺到該框架的功能和靈活性。我鼓勵您自己進一步試驗
Castor
。我想您會像我一樣發現
Castor
那樣有用(那樣有趣)。
結束語
數據綁定是使用
XML
進行數據交換的應用程序中的文檔模型的一個非常不錯的替代方案。它簡化了您的編程,因為您不需要再按照
XML
去考慮問題。相反,您可以直接處理表示應用程序使用的數據的意義的對象。它還提供使內存和處理器性能比使用文檔模型時更好的潛力。
在本文中,我已經討論了一系列越來越復雜的、使用
Castor
框架的數據綁定示例。所有這些示例都使用我所謂的直接(
direct
)數據綁定:開發者根據數據定義類,然后將數據映射為
XML
文檔結構。在下一篇文章中,我將探討另一種方法:模式(
schema
)數據綁定,它使用文檔模式(比如
DTD
、
XML Schema
或另一種形式)并生成與該模式相應的代碼。
Castor
同時支持模式方法和您在本文中看到的直接綁定,所以在后面的文章中您會看到
Castor
的更多信息。我還將看一下致力于
Java
數據綁定(
Java Data Binding
)標準的
JSR-031
的進展并比較這兩種方法的性能。請留心這個領域了解更多關于
Java
中
XML
數據綁定的信息,不久它就會出現在您身邊的
IBM developerWorks
上
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

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