JiBX 1.2, Phần 2: Từ lược đồ XML thành mã Java

Tạo mã Java tùy chỉnh, sạch hơn từ lược đồ XML

Việc tạo mã từ các định nghĩa lược đồ XML được sử dụng rộng rãi cho mọi kiểu trao đổi dữ liệu XML, bao gồm cả các dịch vụ Web. Hầu hết các công cụ liên kết dữ liệu cấu trúc một cách cứng nhắc mã được tạo ra dựa trên lược đồ — ngay cả các khía cạnh của lược đồ không liên quan gì đến ứng dụng của bạn. Trong hướng dẫn này, là phần thứ hai của loạt bài hai phần, sẽ tìm hiểu cách làm thế nào để JiBX 1.2 tạo ra mã sạch hơn qua việc thực hiện diễn dịch lược đồ tốt hơn và loại bỏ các lớp lộn xộn không cần thiết. Bạn cũng sẽ thấy có thể tùy chỉnh mã được tạo ra như thế nào để phù hợp tốt hơn với nhu cầu của bạn, bao gồm các tuỳ chỉnh có thể dễ dàng loại bỏ các thành phần không cần thiết của lược đồ.

Dennis Sosnoski, Nhà tư vấn, Sosnoski Software Solutions, Inc.

Dennis Sosnoski là một nhà tư vấn và nhà trợ giúp đào tạo chuyên về các dịch vụ Web và SOA dựa trên-Java. Kinh nghiệm phát triển phần mềm chuyên nghiệp của ông trải suốt hơn 30 năm qua, với một thập kỉ cuối tập trung vào các công nghệ XML và Java phía máy chủ. Dennis là nhà phát triển hàng đầu về dụng cụ liên kết dữ liệu XML JiBX mã nguồn mở, cũng là một người có duyên nợ với khung công tác của các dịch vụ Web Apache Axis2. Ông cũng là một trong những thành viên của nhóm chuyên gia đặc tả kỹ thuật của Jax-WS 2.0 và JAXB 2.0. Xem trang web của ông để có thông tin về các dịch vụ đào tạo và tư vấn của ông.



20 05 2009

Trước khi bắt đầu

Về hướng dẫn này

JiBX là một công cụ để liên kết dữ liệu XML với các đối tượng Java™. Liên kết dữ liệu JiBX đã được biết đến từ lâu như là cách tiếp cận nhanh nhất và linh hoạt nhất để liên kết mã Java tới XML. Nhưng sự phức tạp của các định nghĩa liên kết JiBX và sự hỗ trợ còn hạn chế của nó với các định nghĩa lược đồ XML đang được sử dụng ngày càng rộng rãi hơn đã làm cho những người dùng thất vọng nhiều lần. May mắn thay, phiên bản 1.2 của JiBX đã tiến một bước dài theo hướng loại bỏ những vấn đề này. Trong hướng dẫn này, bạn sẽ tìm hiểu về việc sử dụng các đặc tính mới của JiBX 1.2 để tạo ra các định nghĩa lược đồ XML một cách dễ dàng từ mã Java hiện có và để đọc và viết các tài liệu khớp với các định nghĩa lược đồ đã tạo ra — hoàn toàn không cần phải đi vào các chi tiết của các định nghĩa liên kết của JiBX. Phần 1 trình bày nửa còn lại của vấn đề: bắt đầu từ mã Java và tạo ra các định nghĩa lược đồ XML.

Mục tiêu

Hướng dẫn này chỉ dẫn cho bạn về quá trình sử dụng JiBX để tạo mã Java từ các định nghĩa lược đồ XML. Đầu tiên, bạn sẽ tìm hiểu cách làm việc một lược đồ đơn giản và tạo ra một mô hình dữ liệu Java mặc định khớp với lược đồ này như thế nào, sau đó sử dụng mô hình dữ liệu để đọc và việt các tài liệu XML. Tiếp theo bạn sẽ thấy cách làm thế nào để có thể sử dụng các tùy chỉnh để sửa đổi việc sinh mã sao cho nó phù hợp tốt hơn với các yêu cầu của bạn. Cuối cùng, bạn sẽ chuyển đến một ví dụ lược đồ chuẩn- công nghiệp phức tạp hơn và khám phá khả năng của các tùy chỉnh để làm đơn giản mô hình dữ liệu đã được sinh ra cho lược đồ đó và cải thiện khả năng sử dụng. Sau khi đọc hướng dẫn này và thực hiện hết các ví dụ được cung cấp, bạn sẽ có khả năng sử dụng JiBX để tạo ra các mô hình dữ liệu Java tùy chỉnh cho các lược đồ riêng của bạn.

Các điều kiện cần có trước

Để hiểu được hướng dẫn này, bạn ít nhất nên có các kiến thức cơ bản về cả mã Java và XML. Bạn không cần phải hiểu biết chi tiết về các định nghĩa lược đồ XML nhưng sự quen thuộc với các lược đồ sẽ giúp bạn hiểu rõ các ví dụ tốt hơn.

Các yêu cầu hệ thống

Để chạy các ví dụ này, bạn cần phải cài đặt:

Các hướng dẫn tải về và cài đặt JiBX có trong hướng dẫn này.


Giới thiệu JiBX

JiBX là một trong nhiều công cụ dùng để chuyển đổi giữa các cấu trúc dữ liệu Java và các tài liệu XML (xem Tài nguyên). Cái làm nên sự khác biệt của JiBX so với những công cụ khác là các đặc tính linh hoạt và hiệu năng. Hiệu năng của JiBX luôn được đánh giá ở vị trí cao nhất trong lĩnh vực này, vượt quá hai lần hoặc hơn nữa so với hiệu năng của các công cụ phổ biến khác (như JAXB 2.0 chẳng hạn). JiBX cũng linh hoạt hơn hơn hầu hết tất cả các công cụ Java-XML khác, khi sử dụng các định nghĩa liên kết để tách các cấu trúc Java khỏi mô tả XML sao cho từng cái có thể được thay đổi độc lập với cái kia.

Với bản phát hành 1.2, JiBX bổ sung thêm các đặc tính hỗ trợ các định nghĩa lược đồ XML. Bạn có thể sử dụng các công cụ kèm theo trong bản phát hành JiBX để tạo ra một định nghĩa lược đồ khớp với mã Java của bạn hoặc để tạo ra mã Java khớp với định nghĩa lược đồ của bạn. Dù bằng cách nào, bạn cũng nhận được một định nghĩa liên kết để cho phép bạn sử dụng JiBX để chuyển đổi giữa các mã Java và các tài liệu XML khớp với định nghĩa lược đồ. Trong hướng dẫn này, bạn sẽ thấy cách làm thế nào để áp dụng kiểu tạo thứ hai: từ định nghĩa lược đồ tạo mã Java.

Cài đặt JiBX

Bạn cần phải cài đặt JiBX trước khi tiếp tục thực hiện hướng dẫn này. Hãy Tải về bản ZIP phân phối mới nhất 1.2.x và giải nén nó vào một chỗ thuận tiện trên hệ thống của bạn. Xong việc, bạn sẽ nhận được một thư mục có tên là jibx, trong đó có chứa tất cả các tệp JiBX JAR, tài liệu hướng dẫn, các ví dụ và thậm chí cả mã nguồn.

Cài đặt mã nguồn các ví dụ của hướng dẫn này

Bây giờ hãy tải về các mã mẫu của hướng dẫn này, cũng được cung cấp dưới dạng một tệp tin ZIP. Cách dễ nhất để cài đặt nó trên hệ thống của bạn là giải nén tệp tin ZIP vào thư mục gốc của bản phân phối JiBX của bạn (hoặc trên Windows®, sao chép thư mục dwcode1 từ bên trong tệp tin ZIP này vào thư mục gốc bản phân phối JiBX của bạn). Điều này sẽ tạo ra một thư mục con dwcode2 trong thư mục jibx, với các tệp tin ví dụ (bao gồm cả các tệp tin build.xml, custom.xml và các tệp tin khác) bên trong thư mục con dwcode2 đó.

Mã mẫu bao gồm một tệp tin xây dựng Ant Apache để tự động hoá cho chạy các công cụ JiBX và xử lý các bước khác có trong các ví dụ. Nếu bạn cài đặt các mã mẫu trực tiếp vào thư mục cài đặt của JiBX, công cụ xây dựng (build) có thể truy cập các tệp JiBX JAR mà không cần thêm bất kỳ cấu hình nào. Nếu bạn cài đặt các mã mẫu ở nơi khác, bạn vẫn có thể sử dụng công cụ xây dựng Ant. Trong trường hợp này, bạn chỉ cần chỉnh sửa tệp tin build.properties bên trong thư mục mã mẫu và thay đổi giá trị của thuộc tính jibx-home thành đường dẫn đến bản cài đặt JiBX của bạn.


Tạo liên kết và mã mặc định từ lược đồ

Rất dễ dàng để tạo ra một định nghĩa liên kết JiBX và mã Java tương ứng từ một định nghĩa lược đồ XML. Bạn sẽ tìm hiểu cách làm thế nào trong phần này.

Giới thiệu về ví dụ lược đồ đơn giản

Như một ví dụ đơn giản, tôi sẽ bắt đầu với một trong những lược đồ được tạo ra trong Phần 1. Listing 1 hiển thị một phiên bản rút gọn của lược đồ này, nhằm biểu diễn một đơn hàng từ một cửa hàng trực tuyến. Lược đồ đầy đủ được cung cấp trong tệp tin starter.xsd trong thư mục của dwcode2 của mã mẫu.

Listing 1. Lược đồ ví dụ đầu tiên
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://jibx.org/starter" elementFormDefault="qualified" 
    targetNamespace="http://jibx.org/starter">
  <xs:simpleType name="shipping">
    <xs:annotation>
      <xs:documentation>Supported shipment methods. The "INTERNATIONAL" shipment
      methods can only be used for orders with shipping addresses outside the U.S., and
      one of these methods is required in this case.</xs:documentation>
    </xs:annotation>
    <xs:restriction base="xs:string">
      <xs:enumeration value="STANDARD_MAIL"/>
      <xs:enumeration value="PRIORITY_MAIL"/>
      <xs:enumeration value="INTERNATIONAL_MAIL"/>
      ...
    </xs:restriction>
  </xs:simpleType>
  <xs:complexType name="item">
    <xs:annotation>
      <xs:documentation>Order line item information.</xs:documentation>
    </xs:annotation>
    <xs:sequence/>
    <xs:attribute type="xs:string" use="required" name="id">
      <xs:annotation>
        <xs:documentation>Stock identifier. This is expected to be 12 characters in
        length, with two leading alpha characters followed by ten decimal digits.
        </xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute type="xs:int" use="required" name="quantity">
      <xs:annotation>
        <xs:documentation>Number of units ordered.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute type="xs:float" use="required" name="price">
      <xs:annotation>
        <xs:documentation>Price per unit.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
  </xs:complexType>
  <xs:complexType name="address">
    <xs:annotation>
      <xs:documentation>Address information.</xs:documentation>
    </xs:annotation>
    <xs:sequence>
      <xs:element type="xs:string" name="street1">
        <xs:annotation>
          <xs:documentation>First line of street information (required).
          </xs:documentation>
        </xs:annotation>
      </xs:element>
      ...
    </xs:sequence>
    <xs:attribute type="xs:string" name="state">
      <xs:annotation>
        <xs:documentation>State abbreviation (required for the U.S. and Canada,
        optional otherwise).</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute type="xs:string" name="postCode">
      <xs:annotation>
        <xs:documentation>Postal code (required for the U.S. and Canada, optional
        otherwise).</xs:documentation>
      </xs:annotation>
    </xs:attribute>
  </xs:complexType>
  <xs:complexType name="customer">
    <xs:annotation>
      <xs:documentation>Customer information.</xs:documentation>
    </xs:annotation>
    <xs:sequence>
      <xs:element type="xs:long" name="customerNumber"/>
      ...
    </xs:sequence>
  </xs:complexType>
  <xs:element type="tns:order" name="order"/>
  <xs:complexType name="order">
    <xs:annotation>
      <xs:documentation>Order information.</xs:documentation>
    </xs:annotation>
    <xs:sequence>
      <xs:element type="xs:long" name="orderNumber"/>
      <xs:element type="tns:customer" name="customer"/>
      <xs:element type="tns:address" name="billTo">
        <xs:annotation>
          <xs:documentation>Billing address information.</xs:documentation>
        </xs:annotation>
      </xs:element>
      ...
      <xs:element type="tns:item" name="item" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute type="xs:date" use="required" name="orderDate">
      <xs:annotation>
        <xs:documentation>Date order was placed with server.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    ...
  </xs:complexType>
</xs:schema>

Tạo mã và liên kết mặc định

Để tạo ra một liên kết JiBX và các lớp Java từ một lược đồ XML, bạn chỉ cần chạy công cụ org.jibx.schema.codegen.CodeGen có trong tệp tin jibx-tools.jar từ bản phân phối JiBX. Bạn có thể chạy công cụ trực tiếp từ dòng lệnh hoặc gián tiếp thông qua một công cụ xây dựng như Ant Apache.

Phần tải về của hướng dẫn bao gồm một kịch bản lệnh Ant là build.xml có đích codegen để thực hiện hoạt động tạo mã.

Để thử điều này, hãy mở một cửa sổ dòng lệnh trong thư mục dwcode2 nơi đã cài đặt bản tải về và gõ ant codegen. Nếu bạn đã cài đặt Ant trên hệ thống của mình và cài đặt mã tải về theo đúng các hướng dẫn, bạn sẽ nhìn thấy kết quả tương tự như được hiển thị trong Hình 1:

Hình 1. Sử dụng công cụ xây dựng Ant
Kết quả đầu ra của công cụ xây dựng Ant để tạo từ lược đồ

Bạn cũng có thể chạy CodeGen trực tiếp từ cửa sổ dòng lệnh. Để làm được điều này, bạn cần phải:

  1. Đưa tên tệp tin jibx-tools.jar vào đường dẫn lớp (classpath) Java của bạn.
  2. Chỉ rõ org.jibx.schema.codegen.CodeGen là lớp được chạy.
  3. Liệt kê (các) định nghĩa lược đồ được tạo ra.

Đích Ant codegen đã cho sử dụng một số tham số bổ sung thêm để ra lệnh cho CodeGen sử dụng thư mục gen/src như là gốc của cấu trúc gói mô hình dữ liệu được tạo ra và xóa hết tất cả các tệp tin hiện có khỏi thư mục đó trước khi thực hiện hoạt động tạo mã. Đây là dòng lệnh Java để sao đúp đích Ant codegen từ một cĐửa sổ dòng lệnh trong thư mục dwcode2 (giả định bạn đã làm theo đúng hướng dẫn cài đặt được đề nghị):

java -cp ../lib/jibx-tools.jar org.jibx.schema.codegen.CodeGen -t gen/src -w starter.xsd

Trên Windows, lệnh đó là:

java -cp ..\lib\jibx-tools.jar org.jibx.schema.codegen.CodeGen -t gen\src -w starter.xsd

Bạn có thể chuyển tới CodeGen nhiều tùy chọn khác từ dòng lệnh. Bạn sẽ xem xét chúng sau trong hướng dẫn này. Bây giờ chúng ta hãy xem mã Java đã tạo ra.

Các tạo phẩm đã tạo ra

Mã được tạo ra bao gồm năm lớp, tương ứng với năm định nghĩa kiểu toàn cục trong lược đồ ở Listing 1. Listing 2 cho thấy một số mẫu của mã được tạo ra với các đoạn trích từ lớp org.jibx.starter.Order và toàn bộ lớp org.jibx.starter.Shipping:

Listing 2. Mã được tạo ra
/**
 * Order information.
 *
 * Schema fragment(s) for this class:
 * <pre>
 * <xs:complexType xmlns:ns="http://jibx.org/starter" 
   xmlns:xs="http://www.w3.org/2001/XMLSchema" name="order">
 *   <xs:sequence>
 *     <xs:element type="xs:long" name="orderNumber"/>
 *     <xs:element type="ns:customer" name="customer"/>
 *     <xs:element type="ns:address" name="billTo"/>
 *     <xs:element type="ns:shipping" name="shipping"/>
 *     <xs:element type="ns:address" name="shipTo" minOccurs="0"/>
 *     <xs:element type="ns:item" name="item" minOccurs="0" maxOccurs="unbounded"/>
 *   </xs:sequence>
 *   <xs:attribute type="xs:date" use="required" name="orderDate"/>
 *   <xs:attribute type="xs:date" name="shipDate"/>
 *   <xs:attribute type="xs:float" name="total"/>
 * </xs:complexType>
 * </pre>
 */
public class Order
{
    private long orderNumber;
    private Customer customer;
    private Address billTo;
    private Shipping shipping;
    private Address shipTo;
    private List<Item> itemList = new ArrayList<Item>();
    private Date orderDate;
    private Date shipDate;
    private Float total;
    ...
    /**
     * Get the 'shipTo' element value. Shipping address information. If missing, the 
     * billing address is also used as the shipping address.
     */
    public Address getShipTo() {
        return shipTo;
    }
    /**
     * Set the 'shipTo' element value. Shipping address information. If missing, the 
     * billing address is also used as the shipping address.
     */
    public void setShipTo(Address shipTo) {
        this.shipTo = shipTo;
    }
    /**
     * Get the list of 'item' element items.
     */
    public List<Item> getItems() {
        return itemList;
    }
    /**
     * Set the list of 'item' element items.
     */
    public void setItems(List<Item> list) {
        itemList = list;
    }
    ...
}
/**
 * Supported shipment methods. The "INTERNATIONAL" shipment methods can only be used
   for orders with shipping addresses outside the U.S., and one of these methods is
   required in this case.
 *
 * Schema fragment(s) for this class:
 * <pre>
 * <xs:simpleType xmlns:xs="http://www.w3.org/2001/XMLSchema" name="shipping">
 *   <xs:restriction base="xs:string">
 *     <xs:enumeration value="STANDARD_MAIL"/>
 *     <xs:enumeration value="PRIORITY_MAIL"/>
 *     <xs:enumeration value="INTERNATIONAL_MAIL"/>
 *     <xs:enumeration value="DOMESTIC_EXPRESS"/>
 *     <xs:enumeration value="INTERNATIONAL_EXPRESS"/>
 *   </xs:restriction>
 * </xs:simpleType>
 * </pre>
 */
public enum Shipping {
    STANDARD_MAIL, PRIORITY_MAIL, INTERNATIONAL_MAIL, DOMESTIC_EXPRESS, 
    INTERNATIONAL_EXPRESS
}

Như bạn có thể thấy từ Listing 2, CodeGen sẽ tự động chuyển đổi tài liệu lược đồ thành Javadocs trong mã đã tạo ra (ở đây hiển thị như là các chú thích đứng trước trong Javadoc của mỗi lớp và như là một phần của các chú thích cho các phương thức getShipTo()setShipTo()). CodeGen theo mặc định cũng sát nhập cả các định nghĩa lược đồ hiện tại vào Javadocs của lớp và đối với các phương thức get/set để truy nhập thuộc tính, nó mô tả thành phần lược đồ tương ứng với thuộc tính ấy.

Đối với các giá trị lặp lại, ví dụ như phần tử mục hàng lặp lại nhiều lần trong định nghĩa kiểu phức hợp (complexType) đơn hàng ở Listing 1, CodeGen tạo ra một danh sách có định kiểu của Java 5 theo mặc định. Đối với các bảng kê hạn chế kiểu đơn giản (simpleType), ví dụ như kiểu vận chuyển (shipping) trong Listing 1, CodeGen tạo ra một kiểu enum của Java 5 theo mặc định. Mã được tạo ra cho cả hai trường hợp này được hiển thị trong Listing 2.

Liên kết JiBX được tạo ra

Bên cạnh mã đã tạo ra, CodeGen cũng đưa ra một định nghĩa liên kết JiBX (là tệp tin binding.xml, trong trường hợp này). Định nghĩa liên kết báo cho trình biên dịch liên kết JiBX biết cách chuyển đổi giữa các lớp Java và XML như thế nào. Các định nghĩa liên kết bao gồm đầy đủ các chi tiết về các phép biến đổi được JiBX thực hiện, do đó chúng nhất định là phải phức tạp. May mắn thay, bạn không cần phải hiểu định nghĩa liên kết để làm việc với JiBX khi sử dụng tạo mã và liên kết của CodeGen, vì vậy hướng dẫn này không trình bày các chi tiết ở đây.


Làm việc với các tài liệu XML

Trong phần này, bạn sẽ tìm hiểu về việc chạy trình biên dịch liên kết JiBX và việc sử dụng JiBX lúc đang chạy thực để làm việc với các tài liệu XML.

Chạy trình biên dịch liên kết JiBX

Để sử dụng định nghĩa liên kết được tạo ra trong khi làm việc với các tài liệu XML, trước hết bạn cần phải chạy công cụ trình biên dịch liên kết JiBX. Trình biên dịch liên kết thêm mã byte (bytecode) vào các tệp tin lớp đã biên dịch của bạn để thực sự triển khai thực hiện các phép biến đổi thành XML và từ XML, như định nghĩa liên kết đã chỉ rõ. Bạn phải chạy trình biên dịch liên kết mỗi khi bạn biên dịch lại lớp Java của bạn hoặc sửa đổi định nghĩa liên kết, vì vậy tốt nhất là thêm bước biên dịch liên kết như là một phần của quá trình xây dựng tiêu chuẩn của các dự án của bạn.

Trình biên dịch liên kết có trong bản phân phối JiBX như là một phần của tệp tin jibx-bind.jar. Tài liệu JiBX cung cấp đầy đủ các chi tiết về các cách khác nhau để chạy trình biên dịch liên kết, bao gồm cả cách làm thế nào để bạn có thể gọi nó khi đang chạy ứng dụng của bạn chứ không phải như là một phần của quá trình xây dựng. JiBX cũng cung cấp các trình cắm thêm cho Eclipse và IntelliJ IDEA, có thể tự động cho chạy trình biên dịch liên kết khi bạn đang làm việc trong các IDE này.

Với mục đích của hướng dẫn này, bạn nên làm đơn giản mọi việc và chỉ cần chạy trình biên dịch liên kết thông qua Ant. Đích compile của tập tin build.xml sẵn sàng liên kết bằng cách biên dịch cả mã đã tạo ra và chương trình thử nghiệm được cung cấp, trong khi đích bind thực sự chạy trình biên dịch liên kết. Hình 2 cho thấy kết quả đầu ra mà bạn sẽ thấy khi bạn chạy đích này, giả định bạn đã chạy các đích codegen. (Bạn cũng có thể chạy lần lượt tất cả ba đích này bằng cách liệt kê chúng theo thứ tự trên dòng lệnh: ant codegen compile bind.)

Hình 2. Các tác vụ compilebind của công cụ xây dựng Ant
Kết quả đầu ra của công cụ xây dựng Ant cho compile và trình biên dịch liên kết

Sử dụng JiBX trong môi trường đang chạy chương trình

Listing 3 cho thấy một tài liệu thử nghiệm đơn giản khớp với lược đồ, có trong mã tải về của hướng dẫn này, là tệp tin:

Listing 3. Tài liệu kiểm tra cho lược đồ đơn hàng
<order orderDate="2008-10-18" shipDate="2008-10-22" xmlns="http://jibx.org/starter">
  <orderNumber>12345678</orderNumber>
  <customer>
    <customerNumber>5678</customerNumber>
    <firstName>John</firstName>
    <lastName>Smith</lastName>
  </customer>
  <billTo state="WA" postCode="98059">
    <street1>12345 Happy Lane</street1>
    <city>Plunk</city>
    <country>USA</country>
  </billTo>
  <shipping>PRIORITY_MAIL</shipping>
  <shipTo state="WA" postCode="98034">
    <street1>333 River Avenue</street1>
    <city>Kirkland</city>
  </shipTo>
  <item quantity="1" price="5.99" id="FA9498349851"/>
  <item quantity="2" price="9.50" id="GC1234905049"/>
  <item quantity="1" price="8.95" id="AX9300048820"/>
</order>

Gói tải về cũng bao gồm một chương trình kiểm tra đơn giản, được hiển thị ở đây như Listing 4, để trình bày việc sử dụng JiBX cho cả hai tài liệu không tuần tự hóa (unmarshalling) và tuần tự hóa (marshalling). Marshalling là quá trình sinh ra một biểu diễn XML của một đối tượng trong bộ nhớ, có thể bao gồm cả các đối tượng được liên kết với đối tượng ban đầu. Unmarshalling là quá trình ngược với tuần tự hóa, tức là xây dựng một đối tượng (và có thể là một đồ thị của các đối tượng liên kết với nhau) trong bộ nhớ từ một biểu diễn XML). Đích run của Ant thi hành chương trình thử nghiệm này, bằng cách sử dụng tài liệu trong Listing 3 làm đầu vào và viết ra một bản sao kết quả tuần tự hóa của tài liệu thành một tệp tin có tên là out.xml.

Listing 4. Chương trình thử nghiệm
public class Test
{
    /**
     * Unmarshal the sample document from a file, compute and set order total, then
     * marshal it back out to another file.
     *
     * @param args
     */
    public static void main(String[] args) {
        if (args.length < 2) {
            System.out.println("Usage: java -cp ... " +
                "org.jibx.starter.Test in-file out-file");
            System.exit(0);
        }
        try {

            // unmarshal customer information from file
            IBindingFactory bfact = BindingDirectory.getFactory(Order.class);
            IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
            FileInputStream in = new FileInputStream(args[0]);
            Order order = (Order)uctx.unmarshalDocument(in, null);

            // compute the total amount of the order
            float total = 0.0f;
            for (Iterator<Item> iter = order.getItems().iterator(); iter.hasNext();) {
                Item item = iter.next();
                total += item.getPrice() * item.getQuantity();
            }
            order.setTotal(new Float(total));

            // marshal object back out to file (with nice indentation, as UTF-8)
            IMarshallingContext mctx = bfact.createMarshallingContext();
            mctx.setIndent(2);
            FileOutputStream out = new FileOutputStream(args[1]);
            mctx.setOutput(out, null);
            mctx.marshalDocument(order);
            System.out.println("Processed order with " +  order.getItems().size() +
                " items and total value " + total);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
            System.exit(1);
        } catch (JiBXException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }
}

Hình 3 hiển thị kết quả mà bạn sẽ thấy khi chạy đích run:

Hình 3. Tác vụ run của công cụ xây dựng Ant
Kết quả đầu ra của công cụ xây dựng Ant từ việc chạy chương trình thử nghiệm

Đây chính là chương trình thử nghiệm giống như đã sử dụng trong Phần 1 của hướng dẫn và nó cũng chịu các hạn chế giống như đã bàn luận trong hướng dẫn ấy. Cũng như trong Phần 1, tệp tin out.xml là kết quả sinh ra bởi tuần tự hóa ngược lại dữ liệu đơn hàng đã nhận được khi không sắp xếp theo thứ tự tài liệu ban đầu.


Giới thiệu các tùy chỉnh CodeGen

Trong phần này, bạn sẽ tìm hiểu những điều căn bản về tùy chỉnh CodeGen để kiểm soát cấu trúc của mã được sinh ra từ một lược đồ đơn giản.

Một ví dụ tùy chỉnh đơn giản

CodeGen hỗ trợ rất nhiều tùy chỉnh về tất cả các khía cạnh về tạo mã và tạo liên kết. Tập hợp các tùy chỉnh sẽ áp dụng được chuyển đến CodeGen dưới dạng một tài liệu XML, có các phần tử lồng nhau, liên hệ với các lược đồ hay các thành phần lược đồ. Listing 5 đưa ra một ví dụ đơn giản:

Listing 5. Ví dụ tuỳ chỉnh đơn giản
<schema prefer-inline="true" show-schema="false" enumeration-type="simple"
  generate-all="false" includes="order item"/>

Tuỳ chỉnh của Listing 5 bao gồm một phần tử lược đồ không có vùng tên với một số thuộc tính khác nhau, cung cấp các tuỳ chỉnh cụ thể sẽ được áp dụng. (Cho đến nay, bạn chỉ đang làm việc với một định nghĩa lược đồ, vì thế có thể sử dụng dạng tuỳ chỉnh rất đơn giản này. Về sau trong hướng dẫn này, bạn sẽ thấy các ví dụ về các tuỳ chỉnh để làm việc với nhiều lược đồ). Thuộc tính tuỳ biến đầu tiên — prefer-inline="true" — ra lệnh cho CodeGen thay thế ngay vào các định nghĩa lược đồ mà chỉ được tham chiếu một lần, thay vì áp dụng hành vi mặc định là giữ chúng dưới dạng các lớp riêng biệt. Thuộc tính thứ hai — show-schema="false" — chặn việc nhúng các định nghĩa lược đồ vào trong Javadoc của lớp. enumeration-type="simple" tạo ra các bảng kê an toàn về kiểu đơn giản hơn các bảng kê của Java 5.

Cặp thuộc tính cuối cùng làm việc cùng với nhau. CodeGen theo mặc định sẽ tạo ra một lớp mức đỉnh riêng cho mỗi định nghĩa kiểu toàn cục trong các lược đồ ở đầu vào, cùng với một phép ánh xạ tương ứng trong liên kết JiBX sẽ sinh ra cho mỗi kiểu phức hợp (complexType). Thuộc tính generate-all="false" thay đổi hành vi mặc định này, chỉ dứt khoát tạo ra các complexType đó được bao gồm cụ thể vào hoặc được tham chiếu từ một kiểu được bao gồm ấy. Sau đó thuộc tính includes="order item" đưa ra các tên sẽ được tạo ra; các tên này đã được lựa chọn để bảo đảm tính tương thích với chương trình thử nghiệm — phần tử <order> là cần phải có, bởi vì đó là phần tử gốc của mỗi cá thể tài liệu và các mục hàng kiểu complexType là cần phải có bởi vì chương trình thử nghiệm chờ đợi sẽ tìm thấy một lớp mức đỉnh riêng biệt cho kiểu này khi nó tính tổng giá trị của đơn hàng.

Bạn có thể thử các tuỳ chỉnh này bằng cách sử dụng tác vụ Ant custgen thay cho tác vụ codegen (hoặc sử dụng ngay tác vụ full, để chạy toàn bộ chuỗi các đích clean custgen compile bind run). Listing 6 cho thấy các đoạn trích của mã được tạo ra mà bạn có thể so sánh với mã được tạo ra theo mặc định như hiển thị trong Listing 2. Các sự khác biệt lớn (ngoài Javadoc của các lớp đã được đơn giản hóa) là ở chỗ lớp Customer bây giờ đã được thay thế trực tiếp vào dòng lệnh và lớp Shipping bây giờ là một lớp lồng bên trong, sử dụng một lớp bảng kê tùy chỉnh, an toàn về kiểu.

Listing 6. Mã được tạo ra với các tuỳ chỉnh
/**
 * Order information.
 */
public class Order
{
    private long orderNumber;
    private long customerCustomerNumber;
    private String customerFirstName;
    private String customerLastName;
    private Address billTo;
    private Shipping shipping;
    private Address shipTo;
    private List<Item> itemList = new ArrayList<Item>();
    private Date orderDate;
    private Date shipDate;
    private Float total;
    ...
    /**
     * Supported shipment methods. The "INTERNATIONAL" shipment methods can only be used
       for orders with shipping addresses outside the U.S., and one of these methods is 
       required in this case.
     */
    public static class Shipping
    {
        private final String value;
        public static final Shipping STANDARD_MAIL = new Shipping(
                "STANDARD_MAIL");
        public static final Shipping PRIORITY_MAIL = new Shipping(
                "PRIORITY_MAIL");
        public static final Shipping INTERNATIONAL_MAIL = new Shipping(
                "INTERNATIONAL_MAIL");
        public static final Shipping DOMESTIC_EXPRESS = new Shipping(
                "DOMESTIC_EXPRESS");
        public static final Shipping INTERNATIONAL_EXPRESS = new Shipping(
                "INTERNATIONAL_EXPRESS");
        private static final String[] values = new String[]{"DOMESTIC_EXPRESS",
                "INTERNATIONAL_EXPRESS", "INTERNATIONAL_MAIL", "PRIORITY_MAIL",
                "STANDARD_MAIL"};
        private static final Shipping[] instances = new Shipping[]{
                DOMESTIC_EXPRESS, INTERNATIONAL_EXPRESS, INTERNATIONAL_MAIL,
                PRIORITY_MAIL, STANDARD_MAIL};

        private Shipping(String value) {
            this.value = value;
        }

        public String toString() {
            return value;
        }

        public static Shipping convert(String value) {
            int index = java.util.Arrays.binarySearch(values, value);
            if (index >= 0) {
                return instances[index];
            } else {
                return null;
            }
        }

        public static Shipping fromValue(String text) {
            Shipping value = convert(text);
            if (value == null) {
                throw new IllegalArgumentException("Value \'" + text
                        + "\' is not allowed");
            } else {
                return value;
            }
        }
    }
}

Nhiều tuỳ chỉnh bổ sung có sẵn để sử dụng với CodeGen. Bạn sẽ thấy một số ví dụ về các tùy chỉnh này sau, trong hướng dẫn này, nhưng để cung cấp một ý tưởng rõ hơn về sức mạnh của các tuỳ chỉnh ấy, cần phải chuyển sang một lược đồ phức tạp hơn.


Thử dùng một lược đồ thực tế

Làm việc với một định nghĩa lược đồ đơn độc là dịp rất tốt để trình bày đơn giản, nhưng nó không đem lại nhiều cảm giác về cách một công cụ hoạt động như thế nào khi áp dụng cho các định nghĩa lược đồ phức tạp, sử dụng rộng rãi trong các ứng dụng mức doanh nghiệp. Bây giờ là lúc để chuyển sang một ví dụ hiện thực hơn, dưới dạng một trong các định nghĩa lược đồ HR-XML tiêu chuẩn-công nghiệp.

Lược đồ TimeCard của HR-XML

Hiệp hội (Consortium) HR-XML là một tổ chức được thành lập để phát triển các tiêu chuẩn mở cho các biểu diễn XML dành cho nguồn nhân lực. Nó đại diện cho hơn 110 công ty thành viên và gần 50 hãng công nghệ được chứng nhận đáp ứng các tiêu chuẩn của nó.

Các lược đồ HR-XML sử dụng cho hướng dẫn này gồm có 157 lược đồ, bao gồm một hỗn hợp của các định nghĩa tài liệu mức đỉnh và các thành phần phổ biến. CodeGen có thể dễ dàng xử lý số lượng các lược đồ này, nhưng số lượng lớn của các lớp được tạo ra và tính phức tạp của các mối quan hệ qua lại giữa chúng sẽ làm mờ tối đi các khía cạnh đáng chú ý hơn trong việc xử lý lược đồ. Để tập trung vào các chi tiết này, một tập con của HR-XML được sử dụng ở đây bao gồm một định nghĩa tài liệu mức đỉnh, dành cho phần tử TimeCard cùng với các thành phần phổ biến được tham chiếu như là một phần của định nghĩa TimeCard— tổng số có bảy định nghĩa lược đồ.

Bạn có thể tìm thấy tập con của các định nghĩa lược đồ HR-XML được sử dụng trong hướng dẫn này trong thư mục hrxml/schemas. Listing 7 chỉ ra một phiên bản đã hiệu chỉnh của lược đồ chính, định nghĩa phần tử TimeCard. Điều này cung cấp một mẫu kiểu dáng lược đồ HR-XML, trong đó sử dụng pha trộn các định nghĩa kiểu toàn cục và lồng nhau và có chứa một dải các cấu trúc lược đồ rộng hơn ví dụ đầu tiên, bao gồm:

  • Các hợp tử (compositor) <xs:choice> (như thấy trong một số complexType được nhúng bên trong định nghĩa TimeCardType).
  • Các hạt (particle) <xs:any> (xem định nghĩa AdditionalDataType gần trên đầu của Listing).
  • Các <xs:simpleType> <union> (xem định nghĩa TimeCardDuration ở cuối của Listing).
  • Các hạn chế <xs:simpleType> không là bảng kê (nonenumeration).
Listing 7. Lược đồ TimeCard của HR-XML
<xs:schema targetNamespace="http://ns.hr-xml.org/2007-04-15" ...
  elementFormDefault="qualified" version="2007-04-15">
  <xs:import namespace="http://www.w3.org/XML/1998/namespace" ...>
  <xs:include schemaLocation="../CPO/EntityIdType.xsd"/>
  ...
  <xs:complexType name="AdditionalDataType" mixed="true">
    ...
    <xs:sequence minOccurs="0" maxOccurs="unbounded">
      <xs:any namespace="##any" processContents="strict" minOccurs="0"
          maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="type" type="xs:string"/>
  </xs:complexType>
 ...
  <xs:element name="TimeCard" type="TimeCardType"/>
  <xs:complexType name="TimeCardType">
    <xs:sequence>
      <xs:element name="Id" type="EntityIdType" minOccurs="0"/>
      <xs:element name="ReportedResource">
        <xs:complexType>
          <xs:choice>
            <xs:element name="Person" type="TimeCardPersonType"/>
            <xs:element name="Resource">
          <xs:complexType>
        <xs:sequence>
         <xs:element name="Id" type="EntityIdType"
           minOccurs="0" maxOccurs="unbounded"/>
         <xs:element name="ResourceName" type="xs:string" minOccurs="0"/>
         <xs:element name="AdditionalData" type="AdditionalDataType" minOccurs="0"
             maxOccurs="unbounded"/>
        </xs:sequence>
        <xs:attribute name="type" type="xs:string"/>
       </xs:complexType>
      </xs:element>
     </xs:choice>
    </xs:complexType>
   </xs:element>
   <xs:element name="ReportedTime" maxOccurs="unbounded">
    <xs:complexType>
     <xs:sequence>
      <xs:element name="PeriodStartDate" type="AnyDateTimeType"/>
      <xs:element name="PeriodEndDate" type="AnyDateTimeType"/>
      <xs:element name="ReportedPersonAssignment" minOccurs="0">
       <xs:complexType>
        <xs:sequence>
         <xs:element name="Id" type="EntityIdType" minOccurs="0"/>
        </xs:sequence>
       </xs:complexType>
      </xs:element>
      <xs:choice maxOccurs="unbounded">
       <xs:element name="TimeInterval">
        <xs:complexType>
         <xs:sequence>
          <xs:element name="Id" type="EntityIdType" minOccurs="0"/>
          <xs:element name="StartDateTime" type="AnyDateTimeType"/>
          <xs:choice>
           <xs:sequence>
            <xs:element name="EndDateTime" type="AnyDateTimeType"/>
            <xs:element name="Duration" type="TimeCardDuration" minOccurs="0"/>
           </xs:sequence>
           <xs:element name="Duration" type="TimeCardDuration"/>
          </xs:choice>
          <xs:element name="PieceWork" minOccurs="0" maxOccurs="unbounded">
           ...
          </xs:element>
          <xs:element name="RateOrAmount" minOccurs="0" maxOccurs="unbounded">
           ...
          </xs:element>
          <xs:element name="Allowance" minOccurs="0" maxOccurs="unbounded">
           ...
          </xs:element>
          ...
         </xs:sequence>
         <xs:attribute name="type" type="xs:string" use="required"/>
         ...
        </xs:complexType>
       </xs:element>
       <xs:element name="TimeEvent">
        ...
       </xs:element>
       <xs:element name="Expense">
        ...
       </xs:element>
       <xs:element name="Allowance">
        ...
       </xs:element>
      </xs:choice>
      ...
     </xs:sequence>
     ...
    </xs:complexType>
   </xs:element>
   ...
  </xs:sequence>
  <xs:attribute ref="xml:lang"/>
 </xs:complexType>
 ...
 <xs:simpleType name="TimeCardDuration">
  <xs:union memberTypes="xs:duration xs:decimal"/>
 </xs:simpleType>
</xs:schema>

Mã được tạo ra cho TimeCard

Tệp tin Ant build.xml trong thư mục hrxml định nghĩa các đích Ant để thử tạo mã cơ sở cho lược đồ TimeCard bao gồm cả hai: việc sinh mã mặc định và một số các ví dụ tuỳ chỉnh (được thảo luận sau). Thư mục sample cũng chứa một chương trình thử nghiệm, org.jibx.hrxml.Test. Nó không sắp xếp theo thứ tự các tài liệu mẫu, sử dụng các lớp của mô hình dữ liệu đã sinh ra và sau đó sắp xếp theo thứ tự trở lại các tài liệu và so sánh kết quả với tài liệu gốc ban đầu. Và có một bộ các tài liệu thử nghiệm từ bản phân phối HR-XML trong thư mục các mẫu. Đích codegen chạy CodeGen sử dụng các mặc định, đích compile biên dịch mã được tạo ra và mã thử nghiệm , đích bind biên dịch liên kết JiBX và đích roundtrip chạy chương trình thử nghiệm trên các tài liệu mẫu. Bạn cũng có thể sử dụng tác vụ full để chạy tất cả các bước này liên tiếp nhau.

Hầu hết các hình thức tạo mã từ lược đồ tạo ra một lớp riêng biệt cho mỗi định nghĩa complexType và cho mỗi bảng kê kiểu simpleTypes. CodeGen thường có khả năng làm giảm số lượng các lớp được tạo ra bằng cách xem xét các tham chiếu và thay thế trực tiếp vào dòng mã các định nghĩa mỗi khi có thể và bằng cách bỏ qua các định nghĩa không sử dụng trong các định nghĩa lược đồ được nhập khẩu hay được bao gồm (included). Trong trường hợp của lược đồ TimeCard, có tổng cộng là 10 kiểu phức hợp (complexTypes) (được đặt tên) toàn cục và 23 kiểu phức hợp (không tên) địa phương bổ sung thêm, cùng với 8 bảng kê kiểu đơn giản (simpleTypes). Mô hình dữ liệu mặc định đã tạo ra bao gồm 15 lớp mức đỉnh và 23 lớp lồng bên trong, ít hơn một chút so với số lượng mà bạn mong đợi sẽ thấy, dựa trên đếm số thành phần lược đồ. Bạn sẽ thấy ở phần sau một vài cách sử dụng tuỳ chỉnh để làm đơn giản hóa hơn nữa mô hình dữ liệu trong các trường hợp mà không phải tất cả các thành phần lược đồ là cần thiết.

Xử lý <xs:choice>

Listing 8 chỉ ra cách CodeGen xử lý một sự lựa chọn giữa hai phần tử trong định nghĩa TimeCardType complexType như thế nào. CodeGen theo mặc định sử dụng một biến lựa chọn để theo dõi lựa chọn nào hiện nay đang áp dụng. Các phương thức set cho các giá trị bao gồm trong sự lựa chọn này cho phép bạn gán một giá trị mới cho lựa chọn hiện tại nhưng ngăn không cho thay đổi trực tiếp sự lựa chọn (nếu bạn cố thử sẽ phát sinh lỗi IllegalStateException). Để thay đổi sự lựa chọn hiện tại một khi nó đã được thiết lập, trước hết bạn cần phải gọi một phương thức clear (ở đây là clearReportedResourceSelect()) để khởi động lại trạng thái lựa chọn đó.

Listing 8.TimeCard của HR-XML-Mã mẫu được tạo ra
/**
 * 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;
    }

Đối với hầu hết các ứng dụng, kiểu xử lý lựa chọn này hoạt động tốt, ngăn không cho người sử dụng cố đặt nhiều hơn một lựa chọn. Tuy nhiên, có thể dùng các tuỳ chỉnh để sửa đổi xử lý lựa chọn mặc định, vì thế nếu bạn không muốn dạng xử lý lựa chọn này, bạn có thể dễ dàng thay đổi nó. Thuộc tính choice-check kiểm soát việc trạng thái lựa chọn của một <xsd:choice> được đánh dấu chọn như thế nào trong mã được tạo ra. Giá trị choice-check="disable" vô hiệu hóa toàn bộ việc đánh dấu chọn và không theo vết trạng thái lựa chọn, bỏ mặc cho người sử dụng thiết lập một và chỉ một giá trị cho mỗi lựa chọn. choice-check="checkset" ứng với xử lý lựa chọn theo mặc định như hiển thị trong Listing 8, tức là chỉ có các phương thức set kiểm tra giá trị đã chọn hiện tại và đưa ra một lỗi ngoại lệ. choice-check="checkboth" cũng kiểm tra trạng thái lựa chọn khi gọi phương thức get, đưa ra một lỗi ngoại lệ nếu phương thức get không khớp với trạng thái lựa chọn hiện tại. Cuối cùng, choice-check="override" thay đổi việc xử lý mặc định thành luôn luôn thay đổi trạng thái hiện tại mỗi khi thiết lập một giá trị lựa chọn bất kỳ chứ không đưa ra một lỗi ngoại lệ khi một trạng thái khác đã được thiết lập trước đó.

Thuộc tính tùy biến choice-exposed hoạt động kết hợp với các thiết lập choice-check để theo vết một trạng thái lựa chọn hiện tại. Một giá trị choice-exposed="false" giữ nguyên tất cả các hằng số trạng thái lựa chọn, giá trị biến trạng thái và phương thức thay đổi trạng thái đều là riêng tư (private), khớp với việc sinh mã theo mặc định như được hiển thị trong Listing 8. choice-exposed="true" làm cho tất cả các thứ kể trên thành công cộng (public), nghĩa là có thể truy nhập rộng rãi, và bổ sung thêm một phương thức get dành cho biến trạng thái. Điều này cho phép bạn dễ dàng sử dụng một câu lệnh switch của Java để thực hiện các mã khác nhau tùy thuộc vào trạng thái hiện tại, thay cho phải dùng nhiều câu lệnh if.

Cả hai thuộc tính này có thể được sử dụng ở bất kỳ mức độ tuỳ chỉnh nào, cho phép bạn thiết lập hành vi cho tất cả các mã được tạo ra ở mức tuỳ chỉnh rộng nhất (outermost) một cách dễ dàng trong khi vẫn giữ lại khả năng làm một cái gì đó khác đi cho từng trường hợp cụ thể một.

Xử lý <xs:any>mixed="true"

Giống như các lược đồ mức doanh nghiệp, các lược đồ HR-XML sử dụng các thành phần lược đồ <xs:any> để tạo ra các điểm mở rộng dành cho dữ liệu mà người sử dụng có thể định nghĩa độc lập với lược đồ gốc ban đầu. CodeGen theo mặc định xử lý các thành phần lược đồ <xs:any> bằng cách sử dụng một đối tượng org.w3c.dom.Element (hoặc danh sách Element, nếu giá trị maxOccurs của <xs:any>là lớn hơn 1). Đối tượng Element có thể được dùng để biểu diễn bất kỳ phần tử XML nào (bao gồm tất cả các thuộc tính, các khai báo vùng tên, và nội dung), vì vậy nó cung cấp mọi khả năng linh hoạt cần thiết để làm việc với bất kỳ tài liệu nào khớp với định nghĩa lược đồ.

Listing 9 cho thấy mã đã tạo ra khớp với một thành phần <xs:any> trong mẫu lược đồ của Listing 7 Bởi vì <xs:any> sử dụng maxOccurs="unbounded", mã được tạo ra sử dụng một danh sách các Element.

Listing 9. <xs:any>-mẫu mã được tạo ra
/**
 * ...
 * 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;
    }
   ...
}

Một số khía cạnh của định nghĩa lược đồ trong Listing 9 được bỏ qua hoặc chỉ được CodeGen xử lý một phần. Đầu tiên, định nghĩa <xs:complexType> bao bên ngoài chỉ rõ mixed="true", có nghĩa là dữ liệu ký tự được phép trộn lẫn nhau với các phần tử được biểu diễn bằng một hạt (particle) <xs:any>. Mô hình dữ liệu được CodeGen tạo ra không có chỗ để chứa nội dung dữ liệu ký tự như vậy, do đó nó sẽ bị loại bỏ khi một tài liệu không được sắp xếp theo thứ tự. Thứ hai là <xs:any> sử dụng thuộc tính processContents="strict", có nghĩa là bất cứ phần tử nào có mặt trong một cá thể tài liệu đều phải có định nghĩa lược đồ riêng của mình. CodeGen không để ý đến thuộc tính này, mặc dù nó có thể mang lại một hiệu quả tương tự bằng cách sử dụng một dạng xử lý <xs:any> khác (sẽ được thảo luận dưới đây). CodeGen cũng không để ý đến các hạn chế vùng tên <xs:any>. Listing 9 sử dụng thuộc tính namespace="##any", có nghĩa là các phần tử khớp với <xs:any> không bị hạn chế vùng tên, nhưng nếu giả dụ như giá trị được thay bằng namespace="##other", thì kết quả cũng sẽ giống nhau.

Bạn có thể sử dụng thuộc tính tuỳ chỉnh any-handling ở bất kỳ mức độ tuỳ chỉnh nào để lựa chọn các cách xử lý <xs:any> khác. Giá trị any-handling="discard" đơn giản sẽ lờ đi các <xs:any> trong mô hình dữ liệu được tạo ra và loại bỏ bất kỳ phần tử nào tương ứng với <xs:any> khi không tuần tự hóa. Giá trị any-handling="dom" khớp với việc xử lý mặc định, tức là sử dụng org.w3c.dom.Element để biểu diễn một phần tử khớp với <xs:any>. Cuối cùng, giá trị any-handling="mapped" tạo ra mã với đòi hỏi phải có một định nghĩa lược đồ toàn cục cho mỗi phần tử khớp với <xs:any> (gần tương ứng với điều kiện lược đồ processContents="strict"). Trong trường hợp cuối cùng này, mô hình dữ liệu sử dụng java.lang.Object biểu diễn một phần tử, mà kiểu thực sự của nó trong thời gian chạy chương trình khớp với định nghĩa lược đồ toàn cục.

Xử lý <xs:simpleType>

Giống như hầu hết các dạng tạo mã từ lược đồ, CodeGen bỏ qua hoặc chỉ xử lý một phần nhiều khía cạnh của các định nghĩa <xs:simpleType>. Các hạn chế <xs:simpleType> là một ví dụ về sự hỗ trợ có giới hạn này. Trong số rất nhiều hạn chế simpleType mà lược đồ định nghĩa (bao gồm cả các hạn chế về chiều dài, về các dải giá trị và thậm chí là cả các mẫu biểu thức chính quy), chỉ có các hạn chế <xs:enumeration> hiện đang được thi hành trong mô hình dữ liệu được tạo ra.

Các <xs:simpleType> <union> hiện nay cũng bị CodeGen bỏ qua. Listing 10 hiển thị mã đã tạo ra khớp với một tham chiếu <xs:union> cùng với các đoạn lược đồ gốc khớp với mã (ở dưới cùng của Listing). Bạn có thể thấy rằng trong Listing 10 mỗi tham chiếu đến một kiểu kết hợp (union) (bao gồm cả kiểu TimeCardDuration hiển thị trong Listing và cả AnyDateTimeType) được biểu diễn bằng một giá trị String đơn giản trong mã được tạo ra.

Listing 10. <xs:union>-mẫu mã được tạo ra và lược đồ gốc
/**
     * 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>

Các sửa đổi của lược đồ

Nếu bạn so sánh các đoạn lược đồ đã nhúng trong Javadoc ở đầu Listing 10 với các đoạn lược đồ thực tế ở dưới cùng của Listing này, bạn sẽ thấy rằng các tham chiếu tới union simpleType trong lược đồ ban đầu đã được thay thế bởi các tham chiếu xs:string trong phiên bản Javadoc. Điều này là có chủ ý và nó là đại diện cho một số kiểu biến đổi cấu trúc lược đồ được CodeGen thực hiện. Một số biến đổi, như loại bỏ <union> simpleType và các mặt (facet) hạn chế của simpleType không phải là <xs:enumeration>, được mã hóa cố định vào trong hoạt động của CodeGen. Các biến đổi khác được các tuỳ chỉnh kiểm soát. Dù bằng cách nào, các đoạn lược đồ bao gồm trong Javadocs luôn luôn cho thấy lược đồ đã chuyển đổi vì đây là những gì thực sự được sử dụng để tạo ra các mã.

Bạn sẽ thấy nhiều kiểu chuyển đổi hơn được các tuỳ chỉnh kiểm soát trong những phần sau của hướng dẫn.


Tuỳ chỉnh mô hình dữ liệu TimeCard

Một ví dụ ở trên, trong hướng dẫn này cho thấy một số tuỳ chỉnh CodeGen đơn giản. Bây giờ khi bạn đã thấy CodeGen xử lý lược đồ TimeCard của HR-XML theo các giá trị thiết lập mặc định như thế nào, đây cũng là lúc để xem xét kỹ một số tuỳ chỉnh mạnh mẽ hơn.

Tùy chỉnh mô hình dữ liệu

Mã mô hình-dữ liệu được CodeGen tạo ra bằng cách sử dụng các thiết lập mặc định có một số điểm yếu. Một điểm yếu là, tất cả các tên kiểu của lược đồ kết thúc bằng Type và chúng cũng được chuyển tiếp thành các tên lớp được tạo ra tương ứng, làm cho các tên dài hơn cần thiết. Tên gói được tạo ra từ vùng tên lược đồ, org.hrxml.ns, là hợp lý, nhưng sẽ tốt hơn nếu có một tên gói chỉ ra rằng mô hình dữ liệu là dành riêng cho các tài liệu TimeCard.

Listing 11 cho thấy khía cạnh rắc rối khác của các lớp mô hình-dữ liệu được tạo ra, trong đó có một lớp java.math.BigInteger được sử dụng để biểu diễn kiểu xs:integer. Đây biểu diễn chính xác nhất cho xs:integer khi sử dụng các lớp Java tiêu chuẩn, nhưng BigInteger là rắc rối khi sử dụng nếu so sánh với kiểu nguyên thủy int đơn giản hay các kiểu đối tượng java.lang.Integer. Thật không may, các lược đồ thường được viết với kiểu xs:integer ngay cả khi xs:int sẽ là thích hợp hơn, do đó các nhà phát triển có thể gặp khó khăn với các giá trị BigInteger trong mã được tạo ra. Đó là điều chắc chắn trong trường hợp này, khi các giá trị thực sự hợp lệ dành cho GenderCode chỉ là các số có một chữ số (như đoạn lược đồ gốc ban đầu đã hiển thị ở dưới cùng của Listing).

Listing 11. Ví dụ tạo 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>

Listing 12 cho thấy một tuỳ chỉnh để cải thiện các khía cạnh vừa nói trên của mô hình dữ liệu được tạo ra. Thuộc tính package="org.hrxml.timecard" cung cấp gói Java sẽ được dùng cho các lớp được sinh ra. Thuộc tính type-substitutions="xs:integer xs:int" định nghĩa các thay thế kiểu lược đồ được CodeGen áp dụng, trong trường hợp này là sử dụng kiểu xs:int ở bất cứ nơi nào xs:integer được tham chiếu trong lược đồ. Bạn có thể định nghĩa nhiều cặp thay thế chỉ cần bổ sung thêm các tên kiểu vào danh sách, sử dụng các khoảng trống làm dấu cách giữa các cặp cũng như giữa các tên kiểu trong từng cặp.

Phần tử name-converter lồng bên trong đặt cấu hình việc xử lý các tên XML được chuyển đổi thành tên Java. Trong trường hợp này, thuộc tính strip-suffixes="Type" ra lệnh cho CodeGen loại bỏ Type bất kể khi nào nó xuất hiện trong phần cuối của một tên. Bạn có thể cung cấp nhiều lựa chọn gạch bỏ khác bằng thuộc tính này, dưới dạng một danh sách phân cách bằng khoảng trống. Bạn cũng có thể sử dụng một thuộc tính strip-prefixes để loại bỏ phần tiền tố không cần thiết khỏi các tên, cùng với nhiều dạng tuỳ chỉnh khác nữa. Thậm chí bạn còn có thể thay thế lớp chuyển đổi tên mặc định bằng một lớp khác do bạn tự thực hiện, nếu bạn muốn làm một cái gì đó thật sự đặc biệt trong các phép chuyển đổi tên. Để có đầy đủ chi tiết về các tùy chọn của trình chuyển đổi tên (name-converter), hãy xem tài liệu hướng dẫn CodeGen của JiBX.

Cuối cùng, phần tử class-decorator lồng bên trong bổ sung thêm một trình trang trí (decorator) cho chuỗi tạo mã. Trong trường hợp này, trình trang trí đã được định nghĩa trước, được cung cấp như là một phần của bản phân phối CodeGen để bổ sung thêm các phương thức hỗ trợ hữu ích đối với các giá trị sưu tập. Bất kỳ các trình trang trí cho việc sinh mã nào, khi đã cấu hình sẽ được CodeGen lần lượt gọi thực hiện khi nó xây dựng mã nguồn cho các lớp mô hình dữ liệu và do đó có cơ hội để sửa đổi hay thêm vào các trường, các phương thức và các lớp những kết cấu được tạo ra bởi CodeGen. Mỗi kết cấu này được chuyển tới trình trang trí dưới dạng một thành phần Cây cú pháp trừu tượng (AST-Abstract Syntax Tree), sử dụng thực thi AST của Eclipse. Các trình trang trí được cung cấp (bao gồm trình trang trí org.jibx.schema.codegen.extend.CollectionMethodsDecorator được sử dụng ở đây để thêm các phương thức và một org.jibx.schema.codegen.extend.SerializableDecorator được sử dụng để thêm giao diện java.io.Serializable và tùy trường hợp, thêm một mã nhận dạng phiên bản cho các lớp mô hình dữ liệu) cung cấp các ví dụ về làm việc với AST của Eclipse để mở rộng CodeGen, đo đó mã nguồn của các lớp này là một điểm khởi đầu tuyệt vời để viết các trình trang trí riêng của bạn.

Listing 12. Ví dụ về các tuỳ chỉnh 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>

Bạn có thể thử tuỳ chỉnh của Listing 12 bằng cách sử dụng đích Ant custgen1 hoặc sử dụng đích custom1 để chạy toàn bộ chuỗi từ tạo ra, biên dịch, liên kết và thử nghiệm. Listing 13 hiển thị kết quả của việc áp dụng các tuỳ chỉnh. Tên lớp TimeCardType đã được thay đổi thành chỉ là TimeCard và ngoài các phương thức get và set của List bây giờ còn được thêm vào các phương thức size, add, indexed get và clear. Trong lớp GenderCode tham chiếu BigInteger đã được thay thế bằng một kiểu nguyên thủy int đơn giản.

Listing 13. Mô hình dữ liệu có tùy chỉnh
/** 
 * 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;
    }
}

Loại bỏ các định nghĩa không sử dụng

Trong ví dụ tuỳ chỉnh đầu tiên, bằng cách sử dụng lược đồ ban đầu đơn giản, bạn đã thấy làm thế nào để kiểm soát các định nghĩa kiểu có trong các mô hình dữ liệu được tạo ra bằng cách sử dụng thuộc tính generate-all="false" để vô hiệu hoá việc tạo ra tất cả các định nghĩa toàn cục và một danh sách includes để bắt buộc tạo ra các định nghĩa cụ thể. Listing 14 trình bày một tuỳ chỉnh đã sửa đổi cho lược đồ TimeCard có bổ sung thêm các thuộc tính này, chỉ có một phần tử TimeCard được đưa vào trong mô hình dữ liệu được tạo ra (tất nhiên, cùng với tất cả mọi thứ sử dụng trong biểu diễn TimeCard).

Listing 14. Tuỳ chỉnh chỉ với các thành phần 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>

Bạn có thể sử dụng đích Ant custgen2 để thử tuỳ chỉnh này với CodeGen hoặc sử dụng đích custom2 để chạy toàn bộ chuỗi từ tạo ra, biên soạn, liên kết và thử nghiệm. Sự thay đổi này làm giảm số lượng các lớp mức đỉnh trong mô hình dữ liệu từ 15 xuống 10 — đó không phải là một khởi đầu kém cho việc đơn giản hoá mô hình dữ liệu.


Tùy chỉnh các thành phần riêng biệt

Cho đến nay, bạn đã thấy chỉ có ví dụ về các tuỳ chỉnh được áp dụng trên toàn bộ tập hợp các lược đồ hoặc cho từng lược đồ riêng. Bạn cũng có thể tùy chỉnh việc CodeGen xử lý từng thành phần cụ thể trong một định nghĩa lược đồ, bao gồm cả các định nghĩa toàn cục và các mục được nhúng vào trong các định nghĩa toàn cục. Các tuỳ chỉnh có sẵn bao gồm việc loại bỏ thành phần khỏi mô hình dữ liệu, việc thay đổi tên lớp hay tên giá trị được sử dụng cho thành phần này và việc thay đổi kiểu lược đồ của thành phần.

Các tuỳ chỉnh để loại bỏ các thành phần khỏi mô hình dữ liệu không hữu ích lắm nếu bạn kiểm soát các lược đồ — trong trường hợp đó, thay đổi lược đồ trực tiếp sẽ luôn luôn đơn giản hơn. Tuy nhiên, các lược đồ trao đổi dữ liệu mức doanh nghiệp thường bao gồm các thành phần đặc biệt mà có thể không thích hợp khi một ứng dụng cụ thể nào đó sử dụng những lược đồ này và những lược đồ này thường không thuộc quyền kiểm soát của bạn. Việc sử dụng tùy chỉnh trong trường hợp này cho phép bạn làm đơn giản hóa mô hình dữ liệu của bạn mà không cần chạm vào các lược đồ được cung cấp.

Các tuỳ chỉnh thành phần

Các tuỳ chỉnh cho các thành phần lược đồ hoạt động bằng cách kết hợp một phần tử tuỳ chỉnh với phần tử định nghĩa lược đồ biểu diễn thành phần ấy. Bạn có thể sử dụng một vài cách tiếp cận khác nhau để thiết lập sự kết hợp giữa phần tử tuỳ chỉnh và phần tử lược đồ vì trong một tình hình cụ thể, cách tiếp cận này có thể thuận lợi hơn cách tiếp cận khác. Tuy nhiên, một phần của kết hợp là cố định: Tên của phần tử tùy chỉnh phải luôn luôn khớp với tên phần tử thành phần của lược đồ. Vì vậy ví dụ như để tùy chỉnh một định nghĩa <xs:element> trong một lược đồ, bạn cần phải sử dụng một phần tử tuỳ chỉnh <element (không có phần vùng tên).

Listing 15 cho thấy một định nghĩa từ một trong các lược đồ khác được tham chiếu bởi TimeCard, nó là một mẫu thích hợp để giải thích tuỳ chỉnh một thành phần riêng biệt. PersonNameType bao gồm một số phần tử xs:string đơn giản, cùng với một số các phần tử khác có cấu trúc phức hợp. Như đã xảy ra, các tài liệu kiểm tra được sử dụng trong mã hướng dẫn không bao gồm bất kỳ cá thể nào của các phần tử Affix hay AlternateScript có kiểu này, do đó chúng là ứng viên phù hợp để loại bỏ nhằm làm đơn giản hóa mô hình dữ liệu đã tạo ra.

Listing 15. Lược đồ 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>

Listing 16 cho thấy một trong các cách định nghĩa tuỳ chỉnh để loại bỏ các phần tử AffixAlternateScript khỏi mô hình dữ liệu. Cách tiếp cận này sử dụng một đặc tả đường dẫn, mà đó là một tập hợp các chỉ thị kiểu XPath để dẫn hướng trong cấu trúc định nghĩa lược đồ. Các bước của đường dẫn được phân tách bằng các ký tự gạch chéo (/) và các bước khớp với các thành phần có tên của định nghĩa lược đồ (các định nghĩa kiểu toàn cục, nhóm hoặc attributeGroup hay các định nghĩa phần tử hoặc thuộc tính cho dù chúng là toàn cục hay không) có thể sử dụng một vị từ [@name=...] để lọc ra một cá thể cụ thể thuộc kiểu thành phần đó.

Listing 16. Tuỳ chỉnh trực tiếp các thành phần không cần thiết
<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>

Trong Listing 16, mỗi đường dẫn được giải thích rõ ràng, kể từ mức lược đồ. Bạn cũng có thể sử dụng các ký tự đại diện trong các đường dẫn. Ký tự * là một bước của đường dẫn khớp với một phần tử bất kỳ trong định nghĩa lược đồ, trong khi ký tự ** khớp với một số bất kỳ các phần tử lồng nhau nào trong định nghĩa lược đồ. Vì vậy, thay cho đường dẫn complexType[@name=PersonNameType]/sequence/element[@name=Affix], bạn có thể sử dụng complexType[@name=PersonNameType]/*/element[@name=Affix], hoặc complexType[@name=PersonNameType]/**/element[@name=Affix]. Tuy nhiên, bạn không thể sử dụng **/element[@name=Affix]— CodeGen yêu cầu bạn xác định rõ ràng thành phần định nghĩa toàn cục tham gia vào một tuỳ chỉnh bất kỳ như là một sự bảo vệ để ngăn chặn khỏi việc áp dụng tuỳ chỉnh không chính xác.

Các tuỳ chỉnh thành phần có thể được lồng nhau, miễn là việc lồng nhau này khớp với cấu trúc định nghĩa lược đồ. Trong trường hợp này, mỗi tuỳ chỉnh chỉ cần xác định đích của nó qua đường dẫn tương đối với tuỳ chỉnh chứa nó. Bạn cũng có thể sử dụng một thuộc tính name="..." của tuỳ chỉnh như là một cách thay thế cho một vị từ [@name=...] trên bước cuối cùng của một đường dẫn và bạn có thể bỏ qua tên phần tử trên bước cuối cùng (do nó luôn luôn giống như tên của phần tử tuỳ chỉnh). Bạn thậm chí có thể tránh sử dụng đường dẫn đầy đủ, thay vào đó là sử dụng lồng nhau, kết hợp với một thuộc tính tên. Listing 17 hiển thị các tuỳ chỉnh giống như Listing 16, nhưng được cấu trúc lại, sử dụng cách tiếp cận thay thế này:

Listing 17. Tùy chỉnh lồng nhau cho các thành phần không cần thiết
<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>

Đơn giản hoá mô hình dữ liệu

Ngoài các thành phần PersonName được sử dụng như là các ví dụ trong tiểu mục ở trên, các lược đồ TimeCard có một số các thành phần phức hợp khác không được sử dụng trong các tài liệu mẫu có trong hướng dẫn này. Bằng cách sử dụng các tùy chỉnh để loại bỏ các thành phần không sử dụng, bạn có thể đơn giản hóa đáng kể mô hình dữ liệu đã tạo ra. Còn có một số trường hợp mà các tên của giá trị Java được CodeGen sử dụng không làm việc tốt. Cụ thể là, các trường hợp mà cùng một tên phần tử giống nhau được sử dụng lặp lại có thể dẫn đến việc các tên giá trị chỉ được phân biệt bằng cách thêm chữ số hậu tố, làm cho khó hiểu, khó sử dụng đúng các giá trị. Xem Listing 10 về một ví dụ, ở đó một cặp trường có tên là durationduration1 được đưa vào trong mã đã tạo ra. Bạn có thể sử dụng một tùy chỉnh để thay đổi một trong số các tên này thành tên gì đó có ý nghĩa hơn.

Listing 18 cho thấy tệp tin custom3.xml từ thư mục hrxml của các mã, trong đó bao gồm tất cả các tuỳ chỉnh này. Ở đây cố ý sử dụng đa dạng nhiều cách tiếp cận để xác định thành phần như đã bàn luận trong tiểu mục trên, cụ thể là các cách lồng nhau, dùng các đường dẫn và dùng các đường dẫn pha trộn với tên. Tùy chỉnh value-name ở phía dưới đáy, sử dụng một thuộc tính value-name="simpleDuration" để thay đổi tên dùng cho duration thứ hai thành một dạng khác, diễn tả hơn.

Listing 18. Đơn giản hoá và làm rõ mô hình dữ liệu 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>

Bạn có thể sử dụng đích Ant custgen3 để thử tuỳ chỉnh này với CodeGen hoặc sử dụng đích custom3 để chạy toàn bộ chuỗi từ tạo ra, biên dịch, liên kết và thử nghiệm. Các tùy chỉnh làm giảm số lượng lớp đã tạo ra còn 9 lớp mức đỉnh và 10 lớp bên trong, tổng cộng là 19 lớp. Số lượng này chính xác bằng một nửa số lượng các lớp có trong mô hình dữ liệu ban đầu được tạo ra không sử dụng các tuỳ chỉnh.


Các tham số dòng lệnh CodeGen

CodeGen hỗ trợ một vài tham số dòng lệnh bổ sung thêm ngoài những tham số được sử dụng trong mã hướng dẫn. Bảng 1 liệt kê các tùy chọn quan trọng nhất:

Bảng 1. Các tùy chọn dòng lệnh CodeGen
LệnhMục đích
-c pathĐường dẫn đến tệp tin tuỳ chỉnh đầu vào.
-n packageGói mặc định cho các định nghĩa lược đồ không có vùng tên (mặc định là gói mặc định).
-p packageGói mặc định cho tất cả các định nghĩa lược đồ (mặc định là sử dụng gói được tạo ra từ mỗi vùng tên của lược đồ).
-s pathĐường dẫn thư mục gốc của lược đồ (mặc định là thư mục hiện tại).
-t pathĐường dẫn thư mục đích cho kết quả được tạo ra (mặc định là thư mục hiện tại).
-vCờ báo xuất ra mọi thông báo trong tiến trình xử lý lệnh.
-wXoá tất cả các tệp tin khỏi thư mục đích trước khi sinh ra kết quả (bỏ qua nếu thư mục đích chính là thư mục hiện tại).

Bạn cũng có thể chuyển các tùy chỉnh toàn cục tới CodeGen dưới dạng các tham số dòng lệnh mà không cần phải tạo một tệp tin tùy chỉnh, bằng cách sử dụng một tiền tố đặc biệt -- trước giá trị thuộc tính tùy chỉnh. Vì vậy, để thiết lập cùng các tùy chọn toàn cục tương tự như đã sử dụng trong các tuỳ chỉnh của Listing 5 mà bạn thêm --prefer-inline=true --show-schema=false --enumeration-type=simple --generate-all=false vào dòng lệnh CodeGen. (Tuy nhiên, bạn không thể xác định danh sách các thành phần lược đồ được đưa vào khi tạo mã theo cách này, vì những thành phần là khác nhau cho mỗi lược đồ cụ thể). Không cần có dấu nháy kép cho giá trị thuộc tính khi bạn sử dụng kỹ thuật này. Nếu bạn muốn thiết lập một tuỳ chỉnh có một danh sách nhiều giá trị, hãy sử dụng dấu phẩy chứ không phải là các dấu cách để phân tách riêng giữa các giá trị. (Vì vậy, ví dụ như nếu muốn bỏ qua các hậu tố tên lược đồ là TypeGroup bạn cần sử dụng tham số dòng lệnh --strip-suffixes=Type,Group).


Tiến xa hơn

Trong hướng dẫn này, bạn đã học những điều căn bản về việc sử dụng JiBX để đầu tiên tạo ra một mô hình dữ liệu Java từ một định nghĩa lược đồ XML và sau đó biến đổi các tài liệu khớp với lược đồ này thành mô hình dữ liệu và ngược lại. Bạn cũng đã xem các ví dụ có sử dụng các tuỳ chỉnh để kiểm soát cách mô hình dữ liệu được tạo ra như thế nào. Có rất nhiều tuỳ chỉnh khác mà bạn có thể sử dụng để kiểm soát các khía cạnh khác nhau của mô hình dữ liệu, ngoài những thứ mà tôi đã trình bày trong hướng dẫn này. Tài liệu JiBX cung cấp đầy đủ các chi tiết về tất cả các lựa chọn tuỳ chỉnh ấy, cùng với nhiều ví dụ về cách tạo mã từ lược đồ.

Các định nghĩa dịch vụ web là một trong những nơi sử dụng chính các lược đồ XML. Hiện nay JiBX có thể được sử dụng trong các chồng dịch vụ Web như Apache Axis2, Apache CXF, XFire và Spring-WS và nó cũng hỗ trợ một máy các dịch vụ Web gọn nhẹ riêng của nó tên là JiBX/WS. Bạn có thể sử dụng các đặc tính tạo mã từ lược đồ đã bàn luận trong hướng dẫn này trong bất kỳ cái nào trong số chồng các dịch vụ Web kể trên, mặc dù hiện tại bạn cần phải trích xuất định nghĩa lược đồ từ định nghĩa dịch vụ của Ngôn ngữ Mô tả Dịch vụ Web (WSDL) trước khi nó có thể được tạo ra. Bạn cũng cần phải đi qua các bước tiếp sau cho từng chồng để đi tới một dịch vụ web hoạt động được. Phiên bản trong tương lai của JiBX sẽ đơn giản hóa quá trình tạo các thực thi dịch vụ web, vì vậy hãy kiểm tra các tài liệu trong bản phân phối JiBX của bạn để tìm hiểu về bất kỳ các đặc tính mới nào trong lĩnh vực này.


Tải về

Mô tảTênKích thước
mã ví dụ cho bài học nàyj-jibx2.zip27KB

Tài nguyên

Học tập

  • JiBX: Binding XML to Java Code: Truy cập vào trang web JiBX để tìm tài liệu API, tin tức dự án và các tài nguyên.
  • "Transform Java classes into Web services using Axis2 and JiBX" (Tyler Anderson, developerWorks, 03.2007): Đọc bài viết hai phần này để tìm hiểu làm thế nào để sử dụng JiBX để định nghĩa một dịch vụ Web từ các lớp Java hiện có.
  • JAXB Reference Implementation: Tìm hiểu về tiêu chuẩn Java JAXB 2.0 cho việc liên kết dữ liệu XML với việc hỗ trợ thời gian chạy được gói trong Java 6 và JRE mới hơn.
  • Data binding with Castor (Brett D. McLaughlin, developerWorks): Tìm hiểu về công cụ liên kết dữ liệu nguồn mở khác trong loạt bài viết này.
  • "Schema for Web Services — Part I: Basic Datatypes" (Dennis Sosnoski, InfoQ, 01.009): Tìm hiểu về một số các hạn chế của lược đồ của các công cụ liên kết dữ liệu (và một vài vấn đề với chính bản thân lược đồ) trong loạt bài này.
  • HR-XML Consortium: Khám phá nhiều hơn về các tiêu chuẩn của hiệp hội HR-XML về việc trao đổi dữ liệu nguồn nhân lực, bao gồm cả lược đồ TimeCard được sử dụng trong hướng dẫn này.
  • Duyệt qua technology bookstore để tìm các sách về chủ đề kỹ thuật này và các chủ đề kỹ thuật khác.
  • developerWorks Java technology zone: Tìm hàng trăm bài viết về mọi khía cạnh của lập trình Java.

Lấy sản phẩm và công nghệ

  • JiBX: Tải về khung công tác JiBX.
  • Ant: Tải về Apache Ant.
  • Sun JDK 1.5 or later: Bạn sẽ cần ít nhất là phiên bản 1.5.0_09 để thực hiện các ví dụ trong hướng dẫn này.
  • IBM® developer kits: Các bộ dụng cụ của nhà phát triển Java của IBM có sẵn cho AIX và Linux.

Thảo luận

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Công nghệ Java, Nguồn mở
ArticleID=388581
ArticleTitle=JiBX 1.2, Phần 2: Từ lược đồ XML thành mã Java
publish-date=05202009