溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

從 XML 到 Java 代碼的數(shù)據(jù)綁定(2):從 XML 數(shù)據(jù)創(chuàng)建類(轉(zhuǎn))

發(fā)布時間:2020-08-08 21:53:55 來源:ITPUB博客 閱讀:148 作者:amyz 欄目:編程語言
從 XML 到 Java 代碼的數(shù)據(jù)綁定(2):從 XML 數(shù)據(jù)創(chuàng)建類(轉(zhuǎn))[@more@]

  數(shù)據(jù)綁定系列的第二篇是如何從 XML 數(shù)據(jù)限制中生成一個 Java 語言。 本文通過完整的代碼展現(xiàn)了如何生成類和代碼,并提供了如何定制您自己版本的建議。 還沒有看過第一篇嗎?第一篇, "對象,無處不在的對象", 解釋了數(shù)據(jù)綁定是如何將 XML 和 Java 語言對象互為轉(zhuǎn)換。它比較了數(shù)據(jù)綁定和其它在 Java 程序中處理 XML 的方法, 并介紹了一個 XML 配置文檔示例。第一部分也介紹了使用 XML Schema 來約束數(shù)據(jù)。

  在深入 Java 程序和 XML 代碼之前,先快速回顧一下本系列第一部分所打下的基礎。

  在第一部分中,我們知道只要可以標識文檔的一組約束,就可以將文檔轉(zhuǎn)換成 Java 對象。那些約束為數(shù)據(jù)提供了接口。如 Web 服務配置文檔示例中所示,XML 文檔應當成為現(xiàn)有 Java 類的一個實例,并且從數(shù)據(jù)約束生成那個類。最后,會看到表示樣本 XML 文檔約束的 XML schema。

  如果對細節(jié)還有疑問,請回顧 第一篇文章.

  打造基礎

  現(xiàn)在,可以著手從 XML schema 創(chuàng)建 Java 類。該類必須準確表示數(shù)據(jù)約束,并提供 Java 應用程序?qū)⑹褂玫暮唵巫x方法和寫方法。開始之前,讓我們先回顧清單 1,查看為 WebServiceConfiguration 文檔定義的 XML schema。

  清單 1. 表示 Web 容器配置文檔數(shù)據(jù)接口的 XML schema

  

  生成代碼

  開始生成 Java 代碼之前,首先必須確定核心類的名稱。將使用 org.enhydra.xml.binding 包中的 SchemaMapper,它是 Enhydra 應用服務器實用程序類集合的一部分。還可以將任何必需的支持類放到這個包中。

  除了類名稱以外,還必須確定用來讀取和創(chuàng)建 XML 的 Java API。如上一篇文章中所討論過的,三種主要選擇是 SAX、DOM 和 JDOM。由于 SAX 僅僅適用于讀取 XML 文檔,因此它不適合創(chuàng)建 XML。由于在打包階段中要將 Java 對象轉(zhuǎn)換為 XML 表示,因此在此階段中需要創(chuàng)建 XML。這就將選擇的范圍縮小到 DOM 和 JDOM。在這兩種選擇都可用的情況下,本例中我選擇使用 JDOM API,僅為了顯示其功能性(并不僅僅因為我是它的合著者之一?。?。

  最后,必須指出如何將 XML schema 提供給 SchemaMapper 類。通常,可以假設類的生成是脫機完成的(通過靜態(tài) main 方法)。僅通過使 main 方法調(diào)用非靜態(tài)方法,還可以從運行時環(huán)境中使用類。做了這些決定后,就可以開始勾畫類的框架了。

  組裝 SchemaMapper 類框架

  要做的第一件事就是為要生成的代碼設置一些基本存儲器。必須能夠從每個執(zhí)行映射的 XML schema 生成多個接口和實現(xiàn)。Java HashMap 正好滿足要求。鍵是接口或?qū)崿F(xiàn)名稱以及映射表中的值,該值是將要輸出到新 Java 程序文件的實際代碼。還需要存儲每對接口/實現(xiàn)的屬性(屬性是在這兩種類之間共享的)。這里,我再次使用 HashMap。其中,鍵是接口名稱。但是,由于每個接口可能有多個屬性,因此該值是另一個具有屬性及其類型的 HashMap。最后,必須存儲 XML schema 的名稱空間,因為 JDOM 將使用這個名稱空間來訪問 XML schema 中的結(jié)構(gòu)。所有這些具體信息都足以初步勾畫出新類的框架,新類在清單 2 中。

  還請注意在清單 2 中已添加了兩個需要使用的基本方法:其中一個方法需要使用 XML schema 的 URL 來執(zhí)行生成(允許它在網(wǎng)絡可訪問 schema 以及本地 schema 下運行),另一個方法將類輸出到指定的目錄中。最后,簡單的 main 方法將 XML schema 看作一個變量,然后執(zhí)行生成。

  清單 2. SchemaMapper 類的框架

  package org.enhydra.xml.binding;import java.io.File;import java.io.FileNotFoundException;import java.io.FileWriter;import java.io.IOException;import java.net.URL;import java.util.HashMap;import java.util.Map;import java.util.Iterator;import java.util.List;    // JDOM classes used for document representationimport org.jdom.Document;import org.jdom.Element;import org.jdom.JDOMException;import org.jdom.Namespace;import org.jdom.NoSuchAttributeException;import org.jdom.NoSuchChildException;import org.jdom.input.SAXBuilder;    /**

SchemaMapper handles generation of Java interfaces and classesfrom an XML schema, essentially allowing data contracts to be set upfor the binding of XML instance documents to Java objects.

@author Brett McLaughlin/public class SchemaMapper {    /** Storage for code for interfaces */private Map interfaces;/** Storage for code for implementations */private Map implementations;/** Properties that accessor/mutators should be created for */protected Map properties;/** XML Schema Namespace */private Namespace schemaNamespace;/** XML Schema Namespace URI */private static final String SCHEMA_NAMESPACE_URI ="http://www.w3.org/1999/xmlSchema";    /***

*Allocate storage and set up defaults.*

*/public SchemaMapper() {interfaces = new HashMap();implementations = new HashMap();properties = new HashMap();schemaNamespace = Namespace.getNamespace(SCHEMA_NAMESPACE_URI);}    /***

*This is the "entry point" for generation of Java classes from an XML*Schema. It allows a schema to be supplied, via URL,*and that schema is used for input to generation.*

** @param schemaURL URL at which XML Schema is located.* @throws IOException - when problems in generation occur.*/public void generateClasses(URL schemaURL) throws IOException {    // Perform generation}    /***

*This will write out the generated classes to the supplied stream.*

** @param directory File to write to (should be a directory).* @throws IOException - when output errors occur.*/public void writeClasses(File dir) throws IOException {    // Perform output to files}    /***

*This provides a static entry point for class generation from*XML Schemas.*

** @param args String[] list of files to parse.*/public static void main(String[] args) {SchemaMapper mapper = new SchemaMapper();try {for (int i=0; i

  In 清單 2中,可以看到對于每個作為自變量傳遞的 XML schema,main 方法都調(diào)用生成過程。首先,方法會生成類。將文件名轉(zhuǎn)換為 URL,并傳遞到 generateClasses(URL schemaURL) 。然后,通過 writeClasses(File dir) 方法將類寫到當前目錄中(轉(zhuǎn)換成 Java File: new File("."))。

  任何其它 Java 類都可以在運行時進行相同的調(diào)用,并生成類。例如,一個定制類裝入器也許能發(fā)現(xiàn)需要打包,確定仍要生成的接口和實現(xiàn),并使用 SchemaMapper 類來執(zhí)行該任務。所有這一切都在運行時完成。因為 generateClasses() 方法需要一個 URL,所以在網(wǎng)絡上使用這個類非常簡單。例如,可以使用它來請求從 HTTP 上公開可用的 XML schema 生成類。

  由于對如何使用類做了盡量少的假設,因此它是一個普通類;程序可以同時在本地和遠程使用它。并且這個類可以當作一組 Java 語言和 XML 實用程序類的一部分,而不是必須以某種特殊形式使用的專用類。這種可重用性原則對 XML 特別關鍵,因為在不同系統(tǒng)上進行網(wǎng)絡訪問和通信是 XML 的基本前提。

  生成類

  構(gòu)建好類的框架后,就可以添加類的主體了。

  我已經(jīng)提到過生成過程具有遞歸性質(zhì)。請記住這一點,需要填充 generateClasses() 方法才能開始??梢允褂?JDOM 讀取 XML schema,然后從 schema 中抽取每個 complexType 元素。對于這些元素中的每一個,如清單 3 所示,遞歸進程從 handleComplexType() 調(diào)用處開始(以后將進一步討論)。

  清單 3. The generateClasses() 方法

  public void generateClasses(URL schemaURL) throws IOException {      /*** Create builder to generate JDOM representation of XML Schema,* without validation and using Apache Xerces.*/SAXBuilder builder = new SAXBuilder();try {Document schemaDoc = builder.build(schemaURL);     // Handle complex typesList complexTypes = schemaDoc.getRootElement().getChildren("complexType",schemaNamespace);for (Iterator i = complexTypes.iterator(); i.hasNext(); ) {     // Iterate and handleElement complexType = (Element)i.next();handleComplexType(complexType);}} catch (JDOMException e) {throw new IOException(e.getMessage());}}    

  為簡便起見,將強調(diào)一些重點,而不是詳細闡述將 schema 轉(zhuǎn)換為 Java 類的整個過程??梢?聯(lián)機查看完整的 SchemaMapper 類,或者可以 下載它。

  生成器必須確定在 XML schema 中找到的每個 complexType 元素是 顯式的(具有“類型”屬性),還是 隱式的(沒有“類型”屬性)。如果類型是顯式的,則類型將成為接口名稱,并且首字母大寫。如果類型是隱式的,那么將根據(jù)特性名稱構(gòu)造接口名稱。清單 4 中顯示了處理這個邏輯的代碼段。(如要了解更多數(shù)據(jù)綁定的定義,請參閱側(cè)欄, 術語解釋。)

  清單 4. 確定接口名稱

              // Determine if this is an explict or implicit typeString type = null;    // Handle extension, if neededString baseType = null;try {    // Assume that we are dealing with an explicit typetype = complexType.getAttribute("name").getValue();} catch (NoSuchAttributeException e) {     /** It is safe with an implicit type to assume that the parent* is of type "element", has no "type" attribute, and that we* can derive the type as the value of the element's "name"* attribute with the word "Type" appended to it.*/try {type = new StringBuffer().append(BindingUtils.initialCaps(complexType.getParent().getAttribute("name").getValue())).append("Type").toString();} catch (NoSuchAttributeException nsae) {    // Shouldn't happen in schema-valid documentsthrow new IOException("All elements must at have a name.");}}   

  因此,根據(jù)代碼中的命名約定, 具有ServiceConfiguration 類型的元素將生成名為 ServiceConfiguration 的 Java 接口。名為 port 但 沒有顯式類型的元素將生成叫做 PortType 的 Java 接口。它采用元素名稱 ( port ),將首字母轉(zhuǎn)成大寫 ( Port ),再加上單詞 Type ,就得到了 PortType 。

  同樣,所有實現(xiàn)類都使用接口名稱,然后添加縮寫 Impl 。所以,最終實現(xiàn)類是 ServiceConfigurationImpl 和 PortTypeImpl 。

  使用這些命名約定,您可以很容易地確定將數(shù)據(jù)約束映射到 Java 接口會得到哪些 Java 類。如果設置了應用程序在運行時裝入類,那么類裝入器或其它實用程序可以迅速確定是否已裝入了所需的類。類裝入器或?qū)嵱贸绦蛑灰獜?XML schema 中找出生成的類名稱,然后嘗試裝入它們就可以了。命名邏輯是事先確定的,因此檢查起來非常方便。

  一旦確定了名稱,就可以生成接口和實現(xiàn)類的框架(請參閱清單 5)。

  清單 5. 生成代碼

  StringBuffer interfaceCode = new StringBuffer();StringBuffer implementationCode = new StringBuffer();    /** Start writing out the interface and implementation class* definitions.*/interfaceCode.append("public interface ").append(interfaceName);    // Add in extension if appropriateif (baseType != null) {interfaceCode.append(" extends ").append(baseType);}interfaceCode.append(" {

");implementationCode.append("public class ").append(implementationName);    // Add in extension if appropriateif (baseType != null) {implementationCode.append(" extends ").append(baseType).append("Impl");}implementationCode.append(" implements ").append(interfaceName).append(" {

");                   // Add in properties and methods                        // Close up interface and implementation classesinterfaceCode.append("}");implementationCode.append("}");   

  實際上,生成屬性和方法是相當簡單的。將接口和相應實現(xiàn)的名稱添加到類的存儲器中,然后是右花括號,它們的作用是結(jié)束類。像這樣成對生成類,而不是單獨生成類,將使同時在接口和實現(xiàn)反映出該過程變得簡單。檢查源代碼(請參閱 參考資料),就可以得到足夠的解釋。

  清單 5中的粗體注釋表示源列表中的多行代碼。在這里精簡代碼是為了保持簡潔。對于正在創(chuàng)建的 XML schema 的每個特性(由 schema attribute 表示),都會將讀方法和寫方法添加到接口和實現(xiàn)(實現(xiàn)還有執(zhí)行方法邏輯的代碼)。同時,將為實現(xiàn)類的代碼添加變量。

  最終結(jié)果就是本系列第一部分中生成的類。可以在這里查看它們,或者與本文中的其余代碼一起下載(請參閱 參考資料):

  • ServiceConfiguration.java
  • ServiceConfigurationImpl.java
  • PortType.java
  • PortTypeImpl.java
  • DocumentType.java
  • DocumentTypeImpl.java
  • WebServiceConfiguration.java
  • WebServiceConfigurationImpl.java

  有兩個輔助程序類也將參與類生成:

  • BindingUtils ,將首字母變成大寫。雖然,可以將這個方法添加到生成器類,但我打算以后在打包和解包類時再使用該方法,所以我將它歸到這個輔助程序類中??梢?聯(lián)機查看 BindingUtils ,或者可以 下載它。
  • DataMapping , SchemaMapper 類用來轉(zhuǎn)換數(shù)據(jù)類型。可以 聯(lián)機查看源碼或者 下載源碼。

  完成包

  如許多其它開放源碼軟件,在這里顯示的數(shù)據(jù)綁定包是一項正在進行中的工作。雖然它已經(jīng)初具規(guī)模,但仍有很大空間可用于添加更多功能和做改進。因此,以這段代碼為基礎,可以有許多方式應用程序中加以衍生。

  可以重新使用該樣本代碼,以將 XML schema 的數(shù)據(jù)約束轉(zhuǎn)換為類型安全的 Java 接口和實現(xiàn)。例如,迄今為止,示例代碼還沒有處理 XML schema 中可能指定的范圍。而對于許多 XML 開發(fā)人員,那些數(shù)據(jù)范圍才是使用 schema 的真正原因。然后,請考慮清單 6 中 Web 服務的擴充 XML schema。

  清單 6. 帶擴充約束的 Web 服務配置

                                    

  清單 6說明了number屬性的類型, 并且在用紅色強調(diào)的幾行中指定了值的合法范圍(1 到 32,767)。當前版本的 SchemaMapper 將忽略這些附加聲明。從 schema 創(chuàng)建 Java 接口和實現(xiàn)類時,沒有必要處理 XML schema 中的 minXXX 和 maxXXX 關鍵字,但它們可以增加相當多的附加驗證。

  請查看清單 7 中的代碼示例,這些代碼是可在實現(xiàn)類中生成的代碼,以確保只有 schema 指定范圍中的值可以作為變量。

  清單 7. 帶范圍檢查的生成代碼

  public class PortTypeImpl implements PortType {private String protocol;private int number;private String protected;                   public void setNumber(int number) {if ((number > 0) && (number <= 32767)) {this.number = number;} else {throw IllegalArgumentException("Argument must be greater than 0and less than or equal to 32767");}}            public int getNumber() {return number;}public void setProtocol(String protocol) {this.protocol = protocol;}public String getProtocol() {return protocol;}public void setProtected(String protected) {this.protected = protected;}public String getProtected() {return protected;}}   

  如果對類提供了非法值,那么清單 7 中的生成代碼塊將拋出一個運行時異常,這樣既確保了類型安全性又確保了范圍安全性。

  可以很方便地將類似于清單 6 和清單 7 中的增強部分添加到我提供的基本代碼中,因為本文中的所有代碼完全都是開放源碼。您也許還想加入 Enhydra 體系結(jié)構(gòu)工作組郵件發(fā)送清單,在該清單中維護和討論了該代碼的未來版本和修訂本??梢詮?Enhydra Web 站點上加入該清單,列在本文的 參考資料中。

  總結(jié)

  目前為止,應該已經(jīng)了解什么是數(shù)據(jù)綁定。已知道使用數(shù)據(jù)綁定的原因,特別是配置信息。已經(jīng)掌握如何創(chuàng)建 XML schema 和配置 Web 容器服務的 XML 實例文檔,而且我們已經(jīng)詳細討論了 org.enhydra.xml.binding.SchemaMapper 類。使用這個類,您可以創(chuàng)建 Java 接口和(該接口的)實現(xiàn),它將管理從 XML 文檔創(chuàng)建的 Java 實例。還知道如何將約束從 XML schema 映射到 Java。

  現(xiàn)在,已經(jīng)可以進入下一部分。在下一部分中,將開始把 XML 文檔實際轉(zhuǎn)換為 Java 對象的過程,其中 Java 對象是生成類的實例。下一篇文章將說明如何完成這個過程,及其逆向過程,以及 org.enhydra.xml.binding.Unmarshaller 和 org.enhydra.xml.binding.Marshaller 類。這兩個類將磁盤上文本的 XML 格式數(shù)據(jù)移到內(nèi)存中的 Java 表示,然后再移回來。

  希望您能喜歡 XML schema 生成類,下次再見!

  以上所有源碼均附在文檔開始處的源代碼下載鏈接中。


向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI