編寫使用這些接口的 Java 代碼
通過在您的應用程序中使用 XMLBean 接口,可以編寫使用新類型的代碼,以便基于該 Schema 對 XML 進行處理。下面的示例將提取有關訂購單 XML 中的每個訂購商品的信息,并計算商品的數量,然后計算其價格總和。請特別注意基于 Schema 生成的類型以及作為 org.openuri.easypo 包的一部分而導入的類型的使用情況。
printItems 方法接收包含訂購單 XML 文件的 File 對象。
package docs.xmlbeans; import java.io.File; import com.bea.xml.*; import org.openuri.easypo.PurchaseOrderDocument; import org.openuri.easypo.PurchaseOrder; import org.openuri.easypo.LineItem; public class POHandler { public static void printItems(File po) throws Exception { /* * 所有 XMLBean Schema 類型都提供嵌套的 Factory 類,您可以使用它 * 將 XML 綁定到該類型,或者創建該類型的新實例。 * 請注意,像該類型一樣的“Document”類型是 XMLBean * 構造,用于表示全局元素。它為您提供了一種 * 獲取和設置整個元素的內容的方法。 * * 此外,只有在所解析的 XML 遵守 * Schema 的情況下,parse 方法才會成功。 */ PurchaseOrderDocument poDoc = PurchaseOrderDocument.Factory.parse(po); /* * PurchaseOrder 類型表示 purchase-order 元素的 * 復雜類型。 */ PurchaseOrder po = poDoc.getPurchaseOrder(); /* * 當某個元素可能作為子元素出現多次時, * Schema 編譯器將生成引用該元素 * 的多個實例的方法。line-item 元素 * 的 maxOccurs 特性值指定為“unbounded”,這意味著 * 它可以根據需要在一個實例中出現多次。 * 因此存在 getLineItemArray 和 setLineItemArray 等方法。 */ LineItem[] lineitems = po.getLineItemArray(); System.out.println("Purchase order has " + lineitems.length + " line items."); double totalAmount = 0.0; int numberOfItems = 0; /* * 循環遍歷 line-item 元素,方法是使用所生成的存取程序 * 獲取子元素的值,如 description、quantity 以及 * price。 */ for (int j = 0; j < lineitems.length; j++) { System.out.println(" Line item:" + j); System.out.println( " Description:" + lineitems[j].getDescription()); System.out.println(" Quantity:" + lineitems[j].getQuantity()); System.out.println(" Price:" + lineitems[j].getPrice()); numberOfItems += lineitems[j].getQuantity(); totalAmount += lineitems[j].getPrice() * lineitems[j].getQuantity(); } System.out.println("Total items:" + numberOfItems); System.out.println("Total amount:" + totalAmount); } }
請注意,基于 Schema 所生成的類型反映了 XML 中的內容:
-
PurchaseOrderDocument,表示全局根元素。
-
getPurchaseOrder 方法,返回包含子元素(其中包括 line-item)的 PurchaseOrderDocument.PurchaseOrder 類型。getLineItemArray 方法,返回包含 line-item 元素的 LineItem 數組。
- 其他方法(如 getQuantity、getPrice 等等)是根據 Schema 的描述自然得出的,其作用是返回 line-item 元素的相應子元素。
- 包含這些類型的包的名稱是從 Schema 的目標名稱空間派生得到的。
所生成的類型名稱的大小寫和標點符號遵從 Java 約定。此外,當該示例解析文件中的 XML 時,其他 parse 方法支持 Java InputStream 對象、Reader 對象等等。
前面的 Java 代碼將在控制臺中打印下列內容:
Purchase order has 3 line items. Line item 0 Description:Burnham's Celestial Handbook, Vol 1 Quantity: 2 Price: 21.79 Line item 1 Description:Burnham's Celestial Handbook, Vol 2 Quantity: 2 Price: 19.89 Total items: 4 Total amount: 41.68
基于 Schema 創建新的 XML 實例
您已經看到 XMLBean 提供一個 "factory" 類,您可以使用該類來創建新的實例。下面的示例創建新的 purchase-order 元素,并添加 customer 子元素。然后它會插入 name 和 address 子元素,從而創建元素,并通過僅調用一次它們的 set 方法來設置它們的值。
public PurchaseOrderDocument createPO() { PurchaseOrderDocument newPODoc = PurchaseOrderDocument.Factory.newInstance(); PurchaseOrder newPO = newPODoc.addNewPurchaseOrder(); Customer newCustomer = newPO.addNewCustomer(); newCustomer.setName("Doris Kravitz"); newCustomer.setAddress("Bellflower, CA"); return newPODoc; }
下面是生成的 XML。請注意,XMLBean 通過使用 "ns1"(或 "namespace 1")前綴,并基于 Schema 來分配正確的名稱空間。在實際工作中,前綴本身并不是真的很重要,它只是用來定義名稱空間的名稱空間 URI (http://openuri.org/easypo)。前綴僅僅是表示它的一個標記。
<ns1:purchase-order xmlns:ns1="http://openuri.org/easypo"> <ns1:customer> <ns1:name>Doris Kravitz</ns1:name> <ns1:address>Bellflower, CA</ns1:address> </ns1:customer> </ns1:purchase-order>
請注意,所有類型(包括基于 Schema 生成的類型)都是對 XmlObject 的繼承,因此都提供了 Factory 類。有關適合使用 XmlObject 的類型系統的概述,請參閱 XMLBean 對內置 Schema 類型的支持。有關參考信息,請參閱 XmlObject Interface。
XMLBean 層次
上面的示例中使用的生成類型實際上是 XMLBean 類型的層次的一部分。這種層次是 XMLBean 用來直觀展現 Schema 視圖的一種方法。層次的頂部是 XmlObject,即 XMLBean 類型的基礎接口。在該級別的下面,有兩個主要的類型類別:生成類型(表示用戶派生的 Schema 類型)和包括類型(表示內置 Schema 類型)。
本主題已介紹了生成類型。有關詳細信息,請參閱從用戶派生的 Schema 類型生成的 Java 類型。內置類型支持
除了基于指定的 Schema 所生成的類型以外,XMLBean 還提供了 46 種特別的 Java 類型,這些類型是對 XML Schema 規范所定義的 46 種內置類型的鏡像。例如,Schema 定義了 xs:string、xs:decimal 和 xs:int,而 XMLBean 提供了 XmlString、XmlDecimal 和 XmlInt。其中的每個類型也是繼承自 XmlObject,該類型與內置 Schema 類型 xs:anyType 對應。
通過 XMLBean,您就可以將 XML 數據作為這些內置類型進行處理。如果您的 Schema 包括一個類型為 xs:int 的元素,XMLBean 將提供一個可以返回 XmlInt 的生成方法。此外,就像您在前面的示例中所看到的,對于大多數類型來說,還將有一個返回純 Java 類型(如 int)的方法。下面的兩行代碼返回 quantity 元素的值,但將其作為其他類型返回。
// 返回以“x”開頭的簡單類型的方法。 XmlInt xmlQuantity = lineitems[j].xgetQuantity(); // 返回純 Java 類型的方法。 int javaQuantity = lineitems[j].getQuantity();
在某種意義上,這兩種 get 方法均導航至 quantity 元素;getQuantity 方法更深入一步,在返回元素值之前,將其轉換為最適合的純 Java 類型。(XMLBean 還為您提供了一種在使用 XML 的同時對它進行驗證的方法。)
如果您對 XML Schema 有所了解,那么您會覺得 XMLBean 類型看起來非常直觀。如果您對 XML Schema 知之甚少,可以使用您自己的 Schema 以及基于它們的 XML 實例來對 XMLBean 進行試驗,以了解詳細的信息。
有關基于 Schema 生成的類型的方法的詳細信息,請參閱 從 Schema 生成的類型的方法。有關 XMLBean 如何表示內置 Schema 類型的詳細信息,請參閱 XMLBean 對內置 Schema 類型的支持。
使用 XQuery 表達式
使用 XMLBean,可以通過 XQuery 查詢 XML 中特定的數據塊。XQuery 有時也稱為“用于 XML 的 SQL”,因為它提供了一種直接從 XML 文檔訪問數據的機制,這與 SQL 所提供的、訪問傳統數據庫數據的機制十分類似。
XQuery 的一些語法是從 XPath 借鑒來的,后者是一種用于在 XML 中指定嵌套數據的語法。下面的示例返回 price 子元素的值小于或等于 20.00 的所有 line-item 元素:
PurchaseOrderDocument doc = PurchaseOrderDocument.Factory.parse(po); /* * XQuery 表達式是由下面兩個字符串組成的。為方便起見, * 在這里分別對它們進行聲明。第一個字符串聲明 * 在查詢表達式中使用的名稱空間前綴;第二個字符串 * 聲明表達式本身。 */ String nsText = "declare namespace po = 'http://openuri.org/easypo'"; String pathText = "$this/po:purchase-order/po:line-item[po:price <= 20.00]"; String queryText = nsText + pathText; XmlCursor itemCursor = doc.newCursor().execQuery(queryText); System.out.println(itemCursor.xmlText());
上述代碼在文檔的開頭創建一個新的光標。從那里,它使用 XmlCursor 接口的 execQuery 方法執行查詢表達式。在本例中,該方法的參數是 XQuery 表達式,該表達式僅僅指出“從我當前的位置,在 purchase-order 元素中導航,并檢索值小于或等于 20.00 的那些 line-item 元素”。$this 變量意味著“當前的位置”。
有關 XQuery 的詳細信息,請參閱 W3C 網站中的 XQuery 1.0:An XML Query Language。
使用 XML 光標
在前面的示例中,您可能已經注意到 XmlCursor 接口。除了提供一種執行 XQuery 表達式的方法外,XML 光標還提供一種用于操作數據的細粒度模型。XML 光標 API 與 DOM 的對象 API 類似,它只是一種指向特定數據塊的方式。因此,就像光標有助于在字處理文檔中導航一樣,XML 光標則定義了 XML 中的一個位置,您可以在該位置對選定的 XML 執行操作。
當沒有可用的 Schema 時,光標是一種在 XML 文檔中移動的理想方法。一旦光標到達了您需要的位置,您就可以使用它執行各種操作。例如,可以設置和獲取值、插入和刪除 XML 的片段、將 XML 的片段復制到文檔的其他部分,以及對 XML 文檔進行其他細粒度更改。
下面的示例使用 XML 光標導航到 customer 元素的 name 子元素。
PurchaseOrderDocument doc = PurchaseOrderDocument.Factory.parse(po); XmlCursor cursor = doc.newCursor(); cursor.toFirstContentToken(); cursor.toFirstChildElement(); cursor.toFirstChildElement(); System.out.println(cursor.getText()); cursor.dispose();
上述代碼執行了哪些操作呢?與前面的示例一樣,代碼加載 File 對象中的 XML。加載文檔之后,代碼在它的起始位置處創建一個光標。移動光標幾次,使它到達嵌套的 name 元素。一旦光標到達該元素,getText 方法便會檢索該元素的值。