程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> JiBX 1.2,第2部分: 從XML模式到Java代碼(二)

JiBX 1.2,第2部分: 從XML模式到Java代碼(二)

編輯:關於JAVA

為 TimeCard 生成的代碼

hrxml 目錄中的 Ant build.xml 文件將定義嘗試為 TimeCard 模式生成基本代碼的 Ant 目標,包括默認生成和幾個自定義示例(稍後討論)。樣例目錄還包含一個測試程序 org.jibx.hrxml.Test。它將使用生成的數據模型類將樣例文檔解組,然後將文檔重新編組並將結果與原始文檔相比較。並且樣例目錄中有一組來自 HR-XML 發行版的測試文檔。codegen 目標將使用默認值運行 CodeGen,compile 將編譯生成的代碼和測試代碼,bind 將編譯 JiBX 綁定,而 roundtrip 將對樣例文檔運行測試程序。您還可以使用 full 任務按順序運行所有這些步驟。

大多數通過模式生成代碼的方式都將為每個 complexType 定義及枚舉 simpleType 生成一個單獨的類。通過在可能的位置檢查引用和內聯定義,並且忽略包括和導入的模式定義中未使用的定義,CodeGen 通常能夠減少生成的類的數量。在 TimeCard 模式中,有總計 10 個全局(命名的)complexType 和附加的 23 個本地(匿名)complexType,以及 8 個枚舉 simpleType。生成的默認數據模型將包含 15 個頂級類和 23 個內部類,要比根據模式組件計算的少一些。您稍後將看到,如果不需要用到全部模式組件,如何使用自定義進一步簡化數據模型。

<xs:choice> 處理

清單 8 顯示了 CodeGen 如何處理 TimeCardType complexType 定義中的兩個元素之間的選擇。默認情況下,CodeGen 將使用一個選擇變量來跟蹤目前處於活動狀態的選擇。選擇中包括的值的 set 方法將允許您寫入目前選擇的新值,但是不能直接更改選擇(如果您嘗試這樣做,則拋出 IllegalStateException)。要在設定後更改目前的選擇,首先需要調用一個清除方法(此處為 clearReportedResourceSelect()),該方法將重置選擇狀態。

清單 8. HR-XML TimeCard 生成的代碼樣例

/**
 * Schema fragment(s) for this class:
 * <pre>
 * <xs:complexType xmlns:ns="http://ns.hr-xml.org/2007-04-15" 
 *  xmlns:ns1="http://www.w3.org/XML/1998/namespace" 
 *  xmlns:xs="http://www.w3.org/2001/XMLSchema" name="TimeCardType">
 *  <xs:sequence>
 *   <xs:element type="ns:EntityIdType" name="Id" minOccurs="0"/>
 *   <xs:element name="ReportedResource">
 *    <xs:complexType>
 *     <xs:choice>
 *      <xs:element type="ns:TimeCardPersonType" name="Person"/>
 *      <xs:element name="Resource">
 *       <!-- Reference to inner class Resource -->
 *      </xs:element>
 *     </xs:choice>
 *    </xs:complexType>
 *   </xs:element>
 *   ...
 */
public class TimeCardType
{
  private EntityIdType id;
  private int reportedResourceSelect = -1;
  private final int REPORTED_RESOURCE_PERSON_CHOICE = 0;
  private final int RESOURCE_CHOICE = 1;
  private TimeCardPersonType reportedResourcePerson;
  private Resource resource;
  ...
  private void setReportedResourceSelect(int choice) {
   if (reportedResourceSelect == -1) {
     reportedResourceSelect = choice;
   } else if (reportedResourceSelect != choice) {
     throw new IllegalStateException(
      "Need to call clearReportedResourceSelect() before changing existing choice");
    }
  }

  /**
   * Clear the choice selection.
   */
  public void clearReportedResourceSelect() {
    reportedResourceSelect = -1;
  }

  /**
   * Check if ReportedResourcePerson is current selection for choice.
   *
   * @return <code>true</code> if selection, <code>false</code> if not
   */
  public boolean ifReportedResourcePerson() {
    return reportedResourceSelect == REPORTED_RESOURCE_PERSON_CHOICE;
  }

  /**
   * Get the 'Person' element value.
   *
   * @return value
   */
  public TimeCardPersonType getReportedResourcePerson() {
    return reportedResourcePerson;
  }

  /**
   * Set the 'Person' element value.
   *
   * @param reportedResourcePerson
   */
  public void setReportedResourcePerson(
      TimeCardPersonType reportedResourcePerson) {
    setReportedResourceSelect(REPORTED_RESOURCE_PERSON_CHOICE);
    this.reportedResourcePerson = reportedResourcePerson;
  }

  /**
   * Check if Resource is current selection for choice.
   *
   * @return <code>true</code> if selection, <code>false</code> if not
   */
  public boolean ifResource() {
    return reportedResourceSelect == RESOURCE_CHOICE;
  }

  /**
   * Get the 'Resource' element value.
   *
   * @return value
   */
  public Resource getResource() {
    return resource;
  }

  /**
   * Set the 'Resource' element value.
   *
   * @param resource
   */
  public void setResource(Resource resource) {
    setReportedResourceSelect(RESOURCE_CHOICE);
    this.resource = resource;
  }

對於大多數應用程序來說,這類選擇處理工作得非常好,可以防止用戶嘗試在一個選擇中設置多個備選值。不過,可以使用自定義修改默認選擇處理,因此如果您不喜歡這種選擇處理形式,您可以輕松地更改它。choice-check 屬性將控制如何在生成的代碼中檢查 <xsd:choice> 的選擇狀態。choice-check="disable" 值將禁用所有檢查並且不跟蹤選擇狀態,讓用戶設置一個選擇狀態並且每個選擇只有一個值。choice-check="checkset" 與 清單 8 中所示的默認處理相符,其中只有 set 方法將檢查目前的設置並拋出異常。choice-check="checkboth" 還將在調用 get 方法時檢查選擇狀態,如果 get 方法與目前的選擇狀態不符,則拋出異常。最後,choice-check="override" 把默認處理修改為在設置選擇的任何值時始終更改目前的狀態,而不是在之前設置其他狀態時拋出異常。

choice-exposed 自定義屬性將與 choice-check 設置結合使用,這些設置將跟蹤目前的選擇狀態。choice-exposed="false" 值將選擇狀態常量、狀態變量值和狀態更改方法全部設置為私有,匹配 清單 8 中所示的默認代碼生成。choice-exposed="true" 將為狀態變量添加 get 方法,使得所有這些內容可以公開訪問。這將允許您輕松地使用 Java switch 語句以根據目前的狀態執行不同的代碼,而不再需要使用多條 if 語句。

這兩個屬性可以在任意級別的自定義中使用,允許您為最外層自定義中所有生成的代碼輕松地設置行為,同時仍然保留根據具體情況執行其他操作的能力。

<xs:any> 和 mixed="true" 處理

和許多企業模式一樣,HR-XML 模式將使用 <xs:any> 模式組件為數據創建擴展點,這些擴展點可以獨立於原始模式,由用戶定義。默認情況下,CodeGen 將使用 org.w3c.dom.Element 對象(如果 <xs:any> 中的 maxOccurs 值大於 1,則使用 Element 列表)處理 <xs:any> 模式組件。Element 對象可用於表示任意一個 XML 元素(包括所有屬性、名稱空間聲明和內容),因此它將提供處理任何匹配模式定義的文檔所需的所有靈活性。

清單 9 顯示了匹配 清單 7 模式樣例的 <xs:any> 組件的生成代碼。由於 <xs:any> 使用 maxOccurs="unbounded",因此生成的代碼將使用一個 Element 列表。

清單 9. <xs:any> 生成的代碼樣例

/**
 * ...
 * Schema fragment(s) for this class:
 * <pre>
 * <xs:complexType xmlns:xs="http://www.w3.org/2001/XMLSchema" mixed="true" 
 *  name="AdditionalDataType">
 *  <xs:sequence>
 *   <xs:any minOccurs="0" maxOccurs="unbounded" processContents="strict" 
 *    namespace="##any"/>
 *  </xs:sequence>
 *  <xs:attribute type="xs:string" name="type"/>
 * </xs:complexType>
 * </pre>
 */
public class AdditionalDataType
{
  private List<Element> anyList = new ArrayList<Element>();
  private String type;

  /**
   * Get the list of sequence items.
   *
   * @return list
   */
  public List<Element> getAny() {
    return anyList;
  }

  /**
   * Set the list of sequence items.
   *
   * @param list
   */
  public void setAny(List<Element> list) {
    anyList = list;
  }
  ...
}

清單 9 中模式定義的某些方面是被忽略的,或者只是由 CodeGen 處理了一部分。首先,封裝的 <xs:complexType> 定義指定了 mixed="true",這意味著允許將字符數據與 <xs:any> 粒子所表示的元素相混合。CodeGen 所生成的數據模型沒有空間來保存這類字符-數據內容,因此在文檔被解組時,這些內容將被丟棄。其次,<xs:any> 將使用 processContents="strict",意味著實例文檔中存在的所有元素都需要擁有自己的模式定義。CodeGen 將忽略此屬性,盡管可能使用其他形式的 <xs:any> 處理(下面將討論)得到類似的效果。CodeGen 還將忽略 <xs:any> 名稱空間限制。清單 9 使用 namespace="##any",表示匹配 <xs:any> 的元素都不受名稱空間的限制,但是舉例來說,如果該值是 namespace="##other",則結果應當相同。

您可以在任意級別的自定義中使用 any-handling 自定義屬性,選擇處理 <xs:any> 的其他方式。值 any-handling="discard" 只忽略生成的數據模型中的 <xs:any>,並在出現解組時丟棄與 <xs:any> 對應的所有元素。any-handling="dom" 將匹配默認處理,使用 org.w3c.dom.Element 表示匹配 <xs:any> 的元素。最後,any-handling="mapped" 將生成代碼,要求每個匹配 <xs:any> 的元素都有一個全局模式定義(大致對應於 processContents="strict" 模式條件)。在這最後一種情況中,數據模型將使用 java.lang.Object 表示元素,並且對象的實際運行時類型匹配全局模式定義。

<xs:simpleType> 處理

和大多數通過模式生成代碼的方式一樣,CodeGen 將忽略或者只部分處理 <xs:simpleType> 定義的許多方面。<xs:simpleType> 限制就是這類有限支持的一個例子。在模式所定義的各種 simpleType 限制(包括長度限制、值范圍,甚至正則表達式模式)中,只有 <xs:enumeration> 限制目前是在生成的數據模型中強制執行的。

CodeGen 目前也忽略 <xs:simpleType> <union>。清單 10 顯示了匹配 <xs:union> 引用的生成的代碼,以及匹配代碼的初始模式片段(位於清單底部)。您可以在清單 10 中看到,每個指向聯合(union)類型(包括清單中所示的 TimeCardDuration 類型和 AnyDateTimeType)的引用在生成的代碼中是用簡單的 String 值表示的。

清單 10. <xs:union> 生成的代碼樣例及原始模式

/**
   * Schema fragment(s) for this class:
   * <pre>
   * <xs:element xmlns:ns="http://ns.hr-xml.org/2007-04-15" 
   *  xmlns:xs="http://www.w3.org/2001/XMLSchema" name="TimeInterval">
   *  <xs:complexType>
   *   <xs:sequence>
   *    <xs:element type="ns:EntityIdType" name="Id" minOccurs="0"/>
   *    <xs:element type="xs:string" name="StartDateTime"/>
   *    <xs:choice>
   *     <xs:sequence>
   *      <xs:element type="xs:string" name="EndDateTime"/>
   *      <xs:element type="xs:string" name="Duration" minOccurs="0"/>
   *     </xs:sequence>
   *     <xs:element type="xs:string" name="Duration"/>
   *    </xs:choice>
   *    ...
   * </pre>
   */
  public static class TimeInterval
  {
    private EntityIdType id;
    private String startDateTime;
    private int choiceSelect = -1;
    private final int END_DATE_TIME_CHOICE = 0;
    private final int DURATION_CHOICE = 1;
    private String endDateTime;
    private String duration;
    private String duration1;
    ...

  ...
  <xsd:element name="TimeInterval">
   <xsd:complexType>
    <xsd:sequence>
     <xsd:element name="Id" type="EntityIdType" minOccurs="0"/>
     <xsd:element name="StartDateTime" type="AnyDateTimeType"/>
     <xsd:choice>
      <xsd:sequence>
       <xsd:element name="EndDateTime" type="AnyDateTimeType"/>
       <xsd:element name="Duration" type="TimeCardDuration" minOccurs="0"/>
      </xsd:sequence>
      <xsd:element name="Duration" type="TimeCardDuration"/>
     </xsd:choice>
     ...
<xsd:simpleType name="TimeCardDuration">
 <xsd:union memberTypes="xsd:duration xsd:decimal"/>
</xsd:simpleType>

模式修改

如果將 清單 10 頂部的 Javadoc 中嵌入的模式片段與清單底部的實際模式片段相比較,您將看到初始模式中的 union simpleType 引用在 Javadoc 版本中已經替換為 xs:string 引用。這是故意的,並且它在 CodeGen 所執行的若干種模式結構轉換中具有代表性。諸如刪除 <union> simpleType 及 simpleType 限制(而非 <xs:enumeration>)之類的轉換都是被硬編碼到 CodeGen 操作中的。其他轉換都受自定義控制。不管使用哪種方法,Javadocs 中包括的模式片段總是顯示轉換後的模式,因為實際上用於生成代碼的就是轉換後的模式。

您將在教程的後幾節中看到受自定義控制的更多類型的轉換。

自定義 數據模型

本教程中先前的 示例 顯示了一些簡單的 CodeGen 自定義。現在,您已經了解了 CodeGen 如何處理帶有默認設置的 HR-XML TimeCard 模式,我們接下來將研究一些更強大的自定義。

自定義數據模型

CodeGen 使用默認設置生成的數據模型代碼有一些弱點。首先,模式類型名稱全都以 Type 為結尾,並且這種情況延續到對應的生成的類名上,導致名稱過長。通過模式名稱空間生成的包名 org.hrxml.ns 是合理的,但是如果包名可以表明該數據模型專門用於 TimeCard 文檔,那麼效果會更好。

清單 11 顯示了生成的數據模型類的另外一個缺點,其中 java.math.BigInteger 用於表示 xs:integer 類型。這是使用標准 Java 類時 xs:integer 的最精確表示,但是與簡單的 int 原語或 java.lang.Integer 對象類型相比,BigInteger 並不好用。糟糕的是,即使使用 xs:int 會更恰當,人們也通常使用 xs:integer 類型編寫模式,因此開發人員可能會在生成的代碼中遇到 BigInteger 值。本例就是這種情況:GenderCode 允許的實際值全都是個位數(如清單底部的原始模式片段所示)。

清單 11. xs:integer 生成示例

/**
 * Schema fragment(s) for this class:
 * <pre>
 * <xs:element xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:integer" 
 *  name="GenderCode"/>
 * </pre>
 */
public class GenderCode
{
  private BigInteger genderCode;

  /**
   * Get the 'GenderCode' element value.
   *
   * @return value
   */
  public BigInteger getGenderCode() {
    return genderCode;
  }

  /**
   * Set the 'GenderCode' element value.
   *
   * @param genderCode
   */
  public void setGenderCode(BigInteger genderCode) {
    this.genderCode = genderCode;
  }
}

 <xsd:simpleType name="GenderCodeType">
  <xsd:annotation>
   <xsd:documentation>Must conform to ISO 5218 - Representation of Human Sexes 
     (0 - Not Known; 1 - Male; 2 - Female; 9 - Not specified)</xsd:documentation>
  </xsd:annotation>
  <xsd:restriction base="xsd:integer">
   <xsd:pattern value="[0129]"/>
  </xsd:restriction>
 </xsd:simpleType>

清單 12 顯示了可以改進生成數據模型的這些缺點的自定義。package="org.hrxml.timecard" 屬性將提供用於生成的類的 Java 包。type-substitutions="xs:integer xs:int" 屬性將定義 CodeGen 所應用的模式類型置換,在本例中使用 xs:int 類型替換模式中引用的 xs:integer。通過向列表中添加更多類型名稱,使用空格分隔每對置換以及其中的類名,您可以定義多對置換。

嵌套的 name-converter 元素將確定如何處理被轉換為 Java 名稱的 XML 名稱。在本例中,strip-suffixes="Type" 屬性將告訴 CodeGen 只要 Type 出現在名稱末尾就刪除它。您可以用一張用空格分隔的列表來提供要通過此屬性刪除的多個備選內容。您也可以使用 strip-prefixes 屬性刪除名稱中不必要的前導文本,以及其他幾種形式的自定義。如果您需要在名稱轉換中執行一些特殊操作,甚至可以用您自己的實現來替換默認的名稱轉換類。有關這些 name-converter 選項的完整信息,請參閱 JiBX CodeGen 文檔。

最後,嵌套的 class-decorator 元素將向代碼生成序列中添加一個修飾符(decorator)。在本例中,修飾符是 CodeGen 發行版中提供的預定義內容,它將添加用於集合值的有用的支持方法。在 CodeGen 構造數據模型類的源代碼時,它將按順序調用所有已配置的代碼生成修飾符,並且這些修飾符可以進行修改或者添加到 CodeGen 生成的字段、方法和類構造中。使用 Eclipse AST 實現,把所有這些構造作為抽象語法樹(Abstract Syntax Tree,AST)組件傳遞給修飾符。提供的修飾符(包括在這裡用於添加方法的 org.jibx.schema.codegen.extend.CollectionMethodsDecorator 修飾符,以及用於向數據模型類中添加 java.io.Serializable 接口和可選的版本 id 的 org.jibx.schema.codegen.extend.SerializableDecorator)演示了如何結合使用 Eclipse AST 以擴展 CodeGen,因此這些類的源代碼是編寫您自己的修飾符的最佳起點。

清單 12. TimeCard 自定義示例

<schema-set xmlns:xs="http://www.w3.org/2001/XMLSchema" package="org.hrxml.timecard"
  type-substitutions="xs:integer xs:int">
 <name-converter strip-suffixes="Type"/>
 <class-decorator class="org.jibx.schema.codegen.extend.CollectionMethodsDecorator"/>
</schema-set>

您可以使用 custgen1 Ant 目標嘗試執行清單 12 中的自定義,也可以使用 custom1 目標運行完整的生成、編譯、綁定及測試操作。清單 13 顯示了應用自定義的結果。TimeCardType 類名已經改為 TimeCard,並且除了 List get 和 set 方法之外,現在還添加了 size、add、indexed get 和 clear 方法。在 GenderCode 類中,BigInteger 引用已經被替換為一個簡單的 int 原語類型。

清單 13. 自定義的數據模型

/** 
 * Schema fragment(s) for this class:
 * <pre>
 * ...
 * </pre>
 */
public class TimeCard
{
  ...
  private List<ReportedTime> reportedTimeList = new ArrayList<ReportedTime>();
  ...
  /** 
   * Get the list of 'ReportedTime' element items.
   * 
   * @return list
   */
  public List<ReportedTime> getReportedTimes() {
    return reportedTimeList;
  }

  /** 
   * Set the list of 'ReportedTime' element items.
   * 
   * @param list
   */
  public void setReportedTimes(List<ReportedTime> list) {
    reportedTimeList = list;
  }

  /** 
   * Get the number of 'ReportedTime' element items.
   * @return count
   */
  public int sizeReportedTime() {
    return reportedTimeList.size();
  }

  /** 
   * Add a 'ReportedTime' element item.
   * @param item
   */
  public void addReportedTime(ReportedTime item) {
    reportedTimeList.add(item);
  }

  /** 
   * Get 'ReportedTime' element item by position.
   * @return item
   * @param index
   */
  public ReportedTime getReportedTime(int index) {
    return reportedTimeList.get(index);
  }

  /** 
   * Remove all 'ReportedTime' element items.
   */
  public void clearReportedTime() {
    reportedTimeList.clear();
  }
  ...
}
/** 
 * Schema fragment(s) for this class:
 * <pre>
 * &lt;xs:element xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:int"
 *  name="GenderCode"/>
 * </pre>
 */
public class GenderCode
{
  private int genderCode;

  /** 
   * Get the 'GenderCode' element value.
   * 
   * @return value
   */
  public int getGenderCode() {
    return genderCode;
  }

  /** 
   * Set the 'GenderCode' element value.
   * 
   * @param genderCode
   */
  public void setGenderCode(int genderCode) {
    this.genderCode = genderCode;
  }
}

清除不使用的定義

在使用初始簡單模式的第一個自定義示例中,您看到了通過使用 generate-all="false" 禁止生成每個全局定義,並使用 includes 列表強制生成特定定義,從而控制生成的數據模型中包括的類型定義。清單 14 顯示了添加了這些屬性的 TimeCard 模式的修改後的自定義,只包含要包括到生成的數據模型中的 TimeCard 元素(當然,還包含 TimeCard 表示所使用的一切內容)。

清單 14. 只包含 TimeCard 組件的自定義

<schema-set xmlns:xs="http://www.w3.org/2001/XMLSchema" package="org.hrxml.timecard"
  type-substitutions="xs:integer xs:int" generate-all="false">
 <name-converter strip-suffixes="Type"/>
 <class-decorator class="org.jibx.schema.codegen.extend.CollectionMethodsDecorator"/>
 <schema name="TimeCard.xsd" includes="TimeCard"/>
</schema-set>

您可以使用 custgen2 Ant 目標嘗試用 CodeGen 使用此自定義,或者使用 custom2 目標運行完整的生成、編譯、綁定及測試。此更改將把數據模型中頂級類的數目從 15 個減少到 10 個 — 這是簡化數據模型的好開端。

自定義獨立組件

到目前為止,您只看到了在整套模式中或者獨立模式中應用的自定義示例。您還可以自定義 CodeGen,使其處理模式定義內 的特定組件,包括全局定義及嵌入到全局定義中的內容項。可用的自定義包括從數據模型中清除組件、更改組件使用的類或值的名稱,以及更改組件的模式類型。

如果要控制模式,則從數據模型中清除組件的自定義不是特別有用 — 在那種情況下,直接更改模式始終更簡單些。但是企業數據交換模式通常包括專用組件,這些專用組件可能不適合用於使用這些模式的特定應用程序,並且這些模式通常不在您的控制范圍內。在這種情況下,使用自定義將允許您簡化數據模型,而無需觸及提供的模式。

組件自定義

模式組件的自定義方式是,把自定義元素與表示組件的模式定義元素關聯在一起。您可以使用多種不同的方法建立自定義與模式元素之間的關聯,因為在特定情況下,一種方法可能比另一種方法更方便。不過,關聯有一部分是固定的:自定義元素的名稱必須始終與模式組件元素名稱相符。因此要自定義模式中的 <xs:element> 定義,您需要使用 <element 自定義元素(沒有名稱空間)。

清單 15 將顯示來自 TimeCard 所引用的其他模式之一的定義,它很好地演示了單個組件的自定義。PersonNameType 包含幾個簡單的 xs:string 元素,以及一些帶有復雜結構的其他元素。教程代碼中使用的測試文檔恰巧不包括這種類型的 Affix 或 AlternateScript 元素的任何實例,因此清除它們以簡化生成的數據模型再合適不過。

清單 15. PersonName 模式

<xsd:complexType name="PersonNameType">
 <xsd:sequence>

  <xsd:element name="FormattedName" type="xsd:string" minOccurs="0"/>
  <xsd:element name="LegalName" type="xsd:string" minOccurs="0"/>
  <xsd:element name="GivenName" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
  <xsd:element name="PreferredGivenName" type="xsd:string" minOccurs="0"/>
  <xsd:element name="MiddleName" type="xsd:string" minOccurs="0"/>
  <xsd:element name="FamilyName" minOccurs="0" maxOccurs="unbounded">
   <xsd:complexType>
    ...
   </xsd:complexType>
  </xsd:element>
  <xsd:element name="Affix" minOccurs="0" maxOccurs="unbounded">
   <xsd:complexType>
    ...
   </xsd:complexType>
  </xsd:element>
  <xsd:element name="AlternateScript" minOccurs="0" maxOccurs="unbounded">
   <xsd:complexType>
    ...
   </xsd:complexType>
  </xsd:element>
 </xsd:sequence>
 <xsd:attribute name="script" type="xsd:string"/>
</xsd:complexType>

清單 16 顯示了一種定義自定義以從數據模型中清除 Affix 和 AlternateScript 元素的方法。這種方法將使用路徑指定,這是一種浏覽模式定義結構的類似 XPath 的方向集合。路徑步驟是通過斜槓(/)字符來分隔的,並且,匹配模式定義的命名組件(全局類型、組或 attributeGroup 定義,或者不管是否是全局性質的元素或屬性定義)的步驟可以使用 [@name=...] 斷言(predicate)來挑選組件類型的特殊實例。

清單 16. 直接自定義不需要的組件

<schema-set ...>
 <schema name="PersonName.xsd">
  <element path="complexType[@name=PersonNameType]/sequence/element[@name=Affix]"
    ignore="true"/>
  <element path=
    "complexType[@name=PersonNameType]/sequence/element[@name=AlternateScript]"
    ignore="true"/>
 </schema>
</schema-set>

在 清單 16 中,每條路徑都是從模式級別完整拼寫的。您還可以在路徑中使用通配符。* 通配符作為路徑的一部分將匹配模式定義中的所有單個元素,而 ** 通配符將匹配模式定義中的任意數目的嵌套元素。因此不要使用 complexType[@name=PersonNameType]/sequence/element[@name=Affix] 路徑,您可以轉而使用 complexType[@name=PersonNameType]/*/element[@name=Affix] 或 complexType[@name=PersonNameType]/**/element[@name=Affix]。可是,您不能使用 **/element[@name=Affix] — CodeGen 要求您明確識別任何自定義中涉及的全局定義組件,作為防止錯誤地應用自定義的安全措施。

只要嵌套匹配模式定義結構,那麼就可以嵌套組件自定義。在這種情況下,每個自定義只需指定與包含的自定義相關的目標。您還可以在自定義中使用 name="..." 屬性替代路徑的最後一步中的 [@name=...] 斷言,並且可以跳過最後一步的元素名稱(因為它必須始終與自定義元素的名稱相同)。您甚至可以完全避免使用路徑,而使用結合了名稱屬性的嵌套。清單 17 顯示的自定義與 清單 16 相同,只是進行了重構,使用了這種備選方法:

清單 17. 嵌套自定義不需要的組件

<schema-set ...>
<schema name="PersonName.xsd">
<complexType name="PersonNameType">
<sequence>
<element name="Affix" ignore="true"/>
<element name="AlternateScript" ignore="true"/>
</sequence>
</complexType>
</schema>
</schema-set>

簡化數據模型

除了在前一小節中用作示例的 PersonName 組件之外,TimeCard 模式還具有大量未在本教程的樣例文檔中使用的其他復雜組件。通過使用自定義清除這些未使用的組件,可以大大簡化生成的數據模型。在某些情況下,CodeGen 所使用的 Java 值名稱無法正常工作。尤其是重復使用同一個元素名稱會導致只能通過數字後綴區分值名稱,因此很難了解如何正確使用值。參見 清單 10 中的示例,其中生成的代碼中包括一對名為 duration 和 duration1 的字段。您可以使用自定義將這些名稱改為更有意義的名稱。

清單 18 將顯示來自代碼的 hrxml 目錄中的 custom3.xml 文件,該文件包括所有這些自定義。該清單有意使用前一小節討論的各種識別組件的方法,結合了嵌套、路徑,以及包含有名稱的路徑。值名稱自定義位於底部,使用 value-name="simpleDuration" 屬性把第二個 duration 使用的名稱改為更具描述性的形式。

清單 18. 簡化 TimeCard 數據模型並使其含義更清晰

<schema-set xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://ns.hr-xml.org/2007-04-15" package="org.hrxml.timecard"
type-substitutions="xs:integer xs:int" generate-all="false">
<name-converter strip-suffixes="Type"/>
<class-decorator class="org.jibx.schema.codegen.extend.CollectionMethodsDecorator"/>
<schema name="UserArea.xsd" excludes="UserArea UserAreaType"/>
<schema name="PersonName.xsd">
<complexType name="PersonNameType">
<sequence>
<element name="Affix" ignore="true"/>
<element name="AlternateScript" ignore="true"/>
</sequence>
</complexType>
</schema>
<schema name="TimeCard.xsd" includes="TimeCard">
<complexType name="TimeCardType">
<element path="**/element[@name=Allowance]" ignore="true"/>
<element path="**/element[@name=PieceWork]" ignore="true"/>
<element path="**/element[@name=TimeEvent]/**/" name="RateOrAmount" ignore="true"/>
<element path="**/choice/[@name=Duration]" value-name="simpleDuration"/>
</complexType>
</schema>
</schema-set>

您可以使用 custgen3 Ant 目標嘗試用 CodeGen 使用此自定義,或者使用 custom3 目標運行完整的生成、編譯、綁定及測試。自定義將把生成的類的數量減少到 9 個頂級類和 10 個內部類,總計 19 個類。這剛好是未使用自定義的初始數據模型中的類數目的一半。

CodeGen 命令行參數

除了在教程代碼中使用的那些命令行參數之外,CodeGen 還支持若干個附加命令行參數。表 1 列出了最重要的選項:

表 1. CodeGen 命令行選項

命令 用途 -c path 輸入自定義文件的路徑 -n package 無名稱空間的模式定義的默認包(默認為默認包) -p package 所有模式定義的默認包(默認為使用從各個模式名稱空間生成的包) -s path 模式根目錄路徑(默認為當前目錄) -t path 生成的輸出的目標目錄路徑(默認為當前目錄) -v 冗余輸出標志 -w 在生成輸出前從目標目錄中刪除所有文件(如果目標目錄就是當前目錄,則忽略)

通過在自定義屬性值之前使用特殊的 -- 前綴,您還可以將全局自定義作為命令行參數傳遞給 CodeGen,而無需創建自定義文件。因此,要設置 清單 5 的自定義中使用的全局選項,您需要向 CodeGen 命令行中添加 --prefer-inline=true --show-schema=false --enumeration-type=simple --generate-all=false(可是,使用這種方法您無法指定要在生成中包括的模式組件列表,因為這些模式組件是專門用於特定模式的)。在使用這項技術時,不需要對屬性值使用引號。如果需要設置獲取多個值的列表的自定義,則使用逗號而不要使用空格作為各個值之間的分隔符(這樣將忽略 Type 和 Group 模式名稱後綴,例如,使用命令行 --strip-suffixes=Type,Group 參數)。

結束語

在本教程中,您首先了解了使用 JiBX 通過 XML 模式定義生成 Java 數據模型,然後在匹配該模式的文檔與數據模型之間來回轉換。還了解了如何使用自定義控制數據模型的生成方式。除了本教程中介紹的自定義之外,還有許多其他自定義可用於控制數據模型的各個方面。JiBX 文檔將提供所有這些自定義選項的完整信息,以及通過模式生成代碼的更多示例。

Web 服務定義是 XML 模式的主要應用之一。JiBX 目前可以在 Apache Axis2、Apache CXF、XFire 和 Spring-WS Web 服務堆棧中使用,並且它還支持它自己的 JiBX/WS 形式的輕量級 Web 服務引擎。您可以對這些 Web 服務堆棧中的任意一個使用本教程討論的通過模式生成代碼的功能,不過目前還需要先通過 Web 服務描述語言(Web Services Description Language,WSDL)服務定義提取模式定義,然後才能生成。您還需要執行針對每個堆棧的附加步驟才能得到有效的 Web 服務。JiBX 的未來版本將簡化創建 Web 服務實現的過程,因此請查閱 JiBX 發行版中的文檔以了解該領域中的所有新功能。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved