本數據綁定系列的第三部分演示瞭如何使用“JSR-031:數據綁定,Sun 數據綁定規範申請”中指定的方法,將 XML 元素和屬性轉換成 Java 對象。這部分主要講述從數據的XML 表示移到應用程序代碼易於使用的 Java 實例。第三部分論及通過將 XML 文檔中的嵌套元素取消編組成 Java 對象、測試和用某些實際示例來使用新的工具。
本系列的目標是演示如何將 XML 元素轉換成 Java 對象,然後可以使用 Java 語言 accessor 和 mutator 方法直接處理 XML 數據。第一部分比較了數據綁定和 Java 應用程序中其它處理 XML 數據的方法,分析了設計決策,還定義了示例 Web 服務配置文檔的 XML 模式。第二部分說明了如何從 XML 模式生成接口和實現,以便符合 XML 模式的 XML 文檔可以轉換成這些生成類的實例。
在第三部分(共四部分)中,將完成基礎知識的講解,並且描述瞭如何精心設計代碼以執行取消編組,取消編組將完成將 XML 轉換成 Java 對象的過程。執行了取消編組後,可以使用測試類(已包括在內)來檢查是否所有部分都已正確組合在一起。本系列的每一部分都建立在其它部分的基礎之上,所以如果您還沒有看過第一和第二部分,您也許會看不懂本文中的一些描述。如果要回顧專門的詞彙表,請參閱術語解釋側欄。
使用第一部分中爲 WebServiceConfiguration 定義的 XML 模式(請參閱更新版本)和第二部分中的接口,即將創建爲配置數據的特定實例提供數據的 XML 文檔。任何符合模式的 XML 文檔都可以編組成 Java 對象。這些對象應該是使用 SchemaMapper 類生成的類的實例。當然,最終結果就是數據綁定。
製作 XML 實例文檔
創建符合模式的 XML 文檔 -- 通常叫做 XML 實例 -- 很簡單。文檔必須只提供與模式中定義的約束相匹配的數據值,如清單 1 所示。
清單 1. 符合示例 XML 模式的 XML 實例文檔
<?xml version="1.0"?>
<webServiceConfiguration xmlns="http://www.enhydra.org"
xmlns:xsi="http://www.w3.org/1999/XMLSchema/instance"
xsi:schemaLocation="http://www.enhydra.org
configuration.xsd"
version="1.1"
name="Unsecured Web Listener" >
<port number="80" protocol="http" protectedPort="false" />
<document root="/usr/local/enhydra/html" index="*.html,*.xml" error="error.html" />
</webServiceConfiguration>
清單 1 中的示例完整地顯示了 WebServiceConfiguration 的實例。實例文檔包括了兩個名稱空間聲明。第一個是缺省名稱空間聲明,請參考 http://www.enhydra.org。這表示所有沒有前綴的元素會分配到此名稱空間。雖然,在本示例中不需要聲明缺省名稱空間,它還給予了文檔一些身份。這個缺省名稱空間有助於將該文檔與其它有相似或等同元素名稱的 XML 文檔區分出來。
定義的另一個名稱空間分配給 xsi 前綴,所以帶該前綴的所有元素都分配到此名稱空間。它 (http://www.w3.org/1999/XMLSchema/instance) 引用“XML 模式實例規範”的 URI。該規範依次定義了 XML 文檔如何引用文檔符合的 XML 模式。最後,schemaLocation 屬性引用 XML 模式。該屬性的第一個變量是受到約束的名稱空間(示例缺省名稱空間,它包括文檔中的每個元素)。第二個變量,用空格與第一個變量分開,引用 XML 模式的實際位置。本例中,模式 configuration.xsd 是一個本地文件,它與文檔在同一個目錄中。也可以通過使用 URL 來引用網絡上任意位置的模式。
在缺省名稱空間中,附加屬性(因爲它們沒有前綴)定義了版本 (1.1) 和名稱 (Unsecured Web Listener)。
接着,聲明瞭模式中的 Port 對象,並定義了它的數據:端口號爲 80,協議是 http。正確取消編組成 Java 代碼後,該文檔就變成了 WebServiceConfigurationImpl 類的實例。然後,Java 代碼可以使用本系列第二部分中設計的接口 WebServiceConfiguration,以使用基本 XML 文檔中的數據。(請注意,可能會在應用程序中執行驗證,如模式驗證側欄中所概述的。)
模式驗證
較新的 XML 語法分析器,如 Apache Xerces 語法分析器的當前發行版,允許對 XML 實例文檔執行模式驗證。驗證允許在程序格式上確保 XML 文檔符合它引用的 XML 模式。請與語法分析器供應商聯繫或參考文檔,以確定語法分析器是否支持模式驗證,其驗證範圍,以及如何打開驗證。
打開前門
正式開始之前,需要提供入口點以取消編組 XML 文檔,該文檔作爲返回 Java 對象的方法的輸入。(由於您會憶起,本例中取消編組的結果只是 Java 對象。)然後,該對象可以轉換成適當的接口,其實,您已經生成了該接口(在本系列第二部分中)。
對於示例 SchemaMapper 類,允許傳入 URL 是最有意義的。由於可以使用網絡資源作爲輸入,而不是隻允許文件名,這就提供了更多選擇。知道了這一點後,下一步就從 URL 創建 JDOM 文檔對象 (org.jdom.Document),然後處理文檔。請查看清單 2 中執行該操作的代碼。
清單 2. 將字符串映射成 Java 指定的類型 /**
*
* This method is the public entry point for unmarshalling an object from
* an XML instance document.
*
*
* @param instanceURL URL for the instance document.
* @return Object - the created Java Object, or
* null if problems occur in a way that does not
* generate an Exception.
* @throws IOException when errors in binding occur.
*/
public static Object unmarshall(URL instanceURL) throws IOException {
// Read in the document
SAXBuilder builder = new SAXBuilder();
try {
Document doc = builder.build(instanceURL);
Element rootElement = doc.getRootElement();
Unmarshaller unmarshaller = new Unmarshaller();
return unmarshaller.getJavaRepresentation(rootElement);
} catch (JDOMException e) {
throw new IOException (e.getMessage());
}
}
清單 2 中的方法是靜態的,允許直接調用它而無需實例化類的實例。由於對 unmarshall 方法的多個調用之間沒有需要共享的數據,因此該方法可以是靜態的。一旦處理了 XML,就將文檔的根元素(以 JDOM 表示)就被傳到執行從 XML 到 Java 對象轉換的內部方法。
轉換數據
我不打算逐行解釋取消編組中使用的完整代碼。可以查看類的完整源碼清單,它基本上是不需加以說明的。但是,在入口點示例中,有一些值得強調的事情。如果創建了適當類的新實例,將使用 XML 文檔提供的值調用 mutator 方法(全都名爲 setXXX)。當然,這將使 XML 數據在實例的 Java 方法中隨處都可用。清單 3 顯示了處理這種查找方法以及隨後調用的代碼片段。
清單 3. unmarshaller 類的入口點
// For each attribute, get its name and call mutator
List attributes = rootElement.getAttributes();
Method[] methods = objectClass.getMethods();
for (Iterator i = attributes.iterator(); i.hasNext(); ) {
Attribute att = (Attribute)i.next();
// Only want attributes for this namespace
if ((!att.getNamespace().equals(ns)) &&
(!att.getNamespace().equals(Namespace.NO_NAMESPACE))) {
continue;
}
// Determine method to call
String methodName = new StringBuffer()
.append("set")
.append(BindingUtils.initialCaps(att.getName()))
.toString();
// Find the method to call, and its parameter type
for (int j=0; j
找到了根元素的屬性,並確定了每個屬性的適用方法。然後,就是處理實際的 java.lang.reflect.Method 對象。XML 屬性的值已確定,並作爲調用的參數傳送到方法。但是,需要解決一個映射問題;XML 文檔中的所有數據都作爲 String 抽取,但傳遞時必須是適當的 Java 類型。清單 4 將一個方法添加到 DataMapping 輔助類中,以滿足轉換的需要。
清單 4 將字符串映射成 Java 特定的類型
/**
*
* This will take the String value supplied and convert it
* to an Object of the type specified in paramType.
*
*
* @param value String value to convert.
* @param paramType Class with type to convert to.
* @return Object - value in correct type.
*/
public static Object getParameter(String value, Class paramType) {
Object ob = null;
String type = paramType.getName();
if (type.equals("java.lang.String")) {
ob = value;
} else if ((type.equals("int")) || (type.equals("java.lang.Integer"))) {
ob = Integer.valueOf(value);
} else if ((type.equals("long")) || (type.equals("java.lang.Long"))) {
ob = Long.valueOf(value);
} else if ((type.equals("float")) || (type.equals("java.lang.Float"))) {
ob = Float.valueOf(value);
} else if ((type.equals("double")) || (type.equals("java.lang.Double"))) {
ob = Double.valueOf(value);
} els