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

JiBX 1.2,第1部分: 從Java代碼到XML模式

編輯:關於JAVA

通過 Java 數據模型與 XML 文檔之間的自定義轉換提高模式質量

XML 模式定義是許多數據交換類型(包括大多數 Web 服務形式)的基礎。但是 XML Schema 是一種十分復雜的標准,並且與處理 Java™ 代碼的工具相比,用於創建和修改模式定義的大多數工具在功能性和易用性方面要遜色一些。您將在本教程 — 共兩部分的 系列教程 的第 1 部分 — 中了解到 JiBX 1.2 的新功能,它將允許您從 Java 代碼入手並輕松地生成優秀的模式定義來匹配數據結構。然後,無論您是否使用 JiBX 數據綁定,都可以直接使用這些模式。

開始之前

關於本教程

JiBX 是 XML 數據與 Java 對象的綁定工具。JiBX 數據綁定一直以來被認為是綁定 Java 代碼與 XML 的最快且最靈活的方法。但是其綁定定義的復雜性及對於廣泛使用的 XML 模式定義的有限支持經常讓用戶感到失望。幸運的是,1.2 版的 JiBX 一直致力於消除這些問題。在本教程中,您將了解如何使用 JiBX 1.2 的新功能從現有的 Java 代碼輕松地生成 XML 模式定義,並讀取和編寫匹配生成的模式定義的 XML 文檔 — 所有這一切都不需要詳細了解 JiBX 綁定定義。第 2 部分 將介紹相反的一面,即從 XML 模式定義生成 Java 代碼。

目標

本教程將指導您完成使用 JiBX 從 Java 代碼生成 XML 模式定義的過程。您將首先看到如何從一個簡單的 Java 數據模型入手,然後生成匹配該模型的默認模式。在此基礎上,您將了解如何輕松地通過應用一系列的自定義來控制 Java 類所使用的實際值及其訪問方法,以及這些類是必要的還是可選的,XML 中使用的名稱及名稱空間,甚至生成的模式定義的結構。在此過程中,您將看到 JiBX 如何利用您在 Javadoc 中的成果自動歸檔模式定義,從而使生成的模式更有價值。在閱讀本教程並且實踐了提供的示例之後,您將可以使用 JiBX 從您自己的 Java 數據結構類生成優秀的 XML 模式定義。

先決條件

要理解本教程,您至少要了解 Java 代碼和 XML 的基礎知識。您不需要詳細了解 XML 模式定義,但是略微熟悉模式將幫助您更好地理解示例。

系統要求

要運行示例,您需要安裝:

以下任意一個工具:

Sun's JDK 1.5.0_09(或更高版本)。

IBM Developer Kit for Java technology 1.5.0 SR3。

最新版本的 Apache Ant 構建工具。

本教程中包括 JiBX 下載及安裝說明。

開始

在本節中,您將初步了解 JiBX,並安裝 JiBX 及教程樣例代碼。

JiBX 簡介

JiBX 是用於轉換 Java 數據結構與 XML 文檔的眾多工具之一。性能與靈活的功能是使 JiBX 脫穎而出的原因。JiBX 的性能一直處於領先地位,在一項、兩項甚至多項參數上優於其他常見工具(例如 JAXB 2.0)。JiBX 還比幾乎所有其他 Java-XML 工具都靈活,它使用綁定定義解除 Java 結構與 XML 表示的綁定,這樣兩者可以相互獨立地進行更改。

在 1.2 發行版中,JiBX 添加了支持 XML 模式定義的主要功能。您可以使用 JiBX 發行版中附帶的工具生成匹配 Java 代碼的模式定義,或者生成匹配模式定義的 Java 代碼。不管怎樣操作,您還將得到一個綁定定義,它將允許您使用 JiBX 轉換 Java 代碼與匹配模式定義的 XML 文檔。在本教程中,您將看到如何應用第一類生成:從 Java 代碼生成模式定義。

安裝 JiBX

在繼續學習本教程之前,您需要先安裝 JiBX。下載 最新的 1.2.x 發行版 ZIP 並將其解壓縮到系統中便於訪問的位置。您將得到名為 jibx 的目錄,其中包含所有的 JiBX JAR、文檔、示例,甚至源代碼。

安裝教程代碼

現在下載教程 樣例代碼,也是以 ZIP 文件的形式提供的。在系統中安裝樣例代碼的最簡單方法是在 JiBX 安裝的根目錄中解壓縮 ZIP(或者對於 Windows®,將 dwcode1 目錄從 ZIP 文件中復制到 JiBX 安裝的根目錄中)。此操作應當在 jibx 目錄中創建 dwcode1 子目錄,而且 dwcode1 子目錄中包含示例文件(包括 build.xml、custom1.xml 和其他文件)。

樣例代碼包括自動運行 JiBX 工具並處理示例涉及的其他步驟的 Apache Ant 構建文件。如果直接將樣例代碼安裝到 JiBX 安裝目錄中,則構建可以訪問 JiBX JAR 而無需任何附加配置。如果在其他位置安裝樣例代碼,則仍然可以使用 Ant 構建。在這種情況下,您只需將 JIBX_HOME 環境變量設為 JiBX 的安裝路徑。或者,您可以編輯樣例代碼目錄中的 build.xml 文件,然後對靠近文件頂部的直接設置 jibx-home 屬性的代碼行取消注釋。

通過 Java 代碼生成 JiBX 綁定定義和相應的 XML 模式定義非常容易。您將在本節中了解具體操作。

Java 示例代碼簡介

作為示例,我將使用表示在線商店訂單的一組 bean 樣式(私有字段、公共 get 和 set 訪問方法)的類的 Java 代碼。 清單 1 顯示了簡短版本的代碼,其中大多數 get/set 方法已省去。完整的樣例代碼位於樣例代碼的 src 目錄中。

清單 1. 基本 Java 代碼

package org.jibx.starter;
 
/**
 * Order information.
 */
public class Order
{
    private long orderNumber;
    private Customer customer;
 
    /** Billing address information. */
    private Address billTo;
    private Shipping shipping;
 
    /** Shipping address information. If missing, the billing address is also used as the
     shipping address. */
    private Address shipTo;
    private List<Item> items;
 
    /** Date order was placed with server. */
    private Date orderDate;
 
    /** Date order was shipped. This will be <code>null</code> if the order has not
     yet shipped. */
    private Date shipDate;
    private Float total;
 
    public long getOrderNumber() {
        return orderNumber;
    }
    ...
}
/**
 * Customer information.
 */
public class Customer
{
    private long customerNumber;
 
    /** Personal name. */
    private String firstName;
 
    /** Family name. */
    private String lastName;
 
    /** Middle name(s), if any. */
    private List<String> middleNames;
    ...
}
/**
 * Address information.
 */
public class Address
{
    /** First line of street information (required). */
    private String street1;
 
    /** Second line of street information (optional). */
    private String street2;
    private String city;
 
    /** State abbreviation (required for the U.S. and Canada, optional otherwise). */
    private String state;
 
    /** Postal code (required for the U.S. and Canada, optional otherwise). */
    private String postCode;
 
    /** Country name (optional, U.S. assumed if not supplied). */
    private String country;
    ...
}
/**
 * Order line item information.
 */
public class Item
{
    /** Stock identifier. This is expected to be 12 characters in length, with two
     leading alpha characters followed by ten decimal digits. */
    private String id;
 
    /** Text description of item. */
    private String description;
 
    /** Number of units ordered. */
    private int quantity;
 
    /** Price per unit. */
    private float price;
    ...
}
/**
 * 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 enum Shipping
{
    STANDARD_MAIL, PRIORITY_MAIL, INTERNATIONAL_MAIL, DOMESTIC_EXPRESS,
    INTERNATIONAL_EXPRESS
}

生成默認綁定和模式

要通過一些 Java 類生成 JiBX 綁定和 XML 模式,首先需要編譯這些類,然後運行 JiBX 發行版的 jibx-tools.jar 中附帶的 org.jibx.binding.generator.BindGen 工具。您可以通過命令行直接運行該工具,也可以通過 Ant 之類的構建工具間接運行該工具。

本教程的下載部分包括 Ant build.xml 腳本,其中 compile 目標用於編譯示例代碼,bindgen 目標用於在編譯後的代碼上運行 BindGen 程序。

要嘗試使用此腳本,請打開已安裝下載的 dwcode1 目錄中的控制台,然後輸入 ant compile bindgen。如果系統中安裝了 Ant 並且根據說明安裝了下載代碼,則應當會看到類似圖 1 中所示的輸出:

圖 1. 使用 Ant 構建

 

您還可以直接從控制台運行 BindGen。為此,需要在 Java 類路徑中包括 jibx-tools.jar,以及用作生成輸入的編譯後的類文件路徑。如果需要復制 Ant bindgen 目標的效果,則還需要在命令行中傳遞類的源文件的根目錄。最後,需要列出希望用於生成的根類。在 UNIX® 和 Linux® 系統中,用於在 dwcode1 目錄(假定您遵循了推薦安裝說明)中通過控制台復制 Ant bindgen 目標的 Java 命令行(包含單獨的一行代碼,即使它在顯示屏中顯示為多行)為:

java -cp ../lib/jibx-tools.jar:bin org.jibx.binding.generator.BindGen 
   -s src org.jibx.starter.Order

On Windows, the command (a single line, regardless of its appearance here) is:

java -cp ..\lib\jibx-tools.jar;bin org.jibx.binding.generator.BindGen 
   -s src org.jibx.starter.Order

許多其他選項可以通過命令行傳遞給 BindGen。您稍後將在教程中看到這些選項。現在,讓我們看一看生成的模式。

生成的工件

清單 2 顯示了通過 BindGen(即 starter.xsd)生成的模式輸出,其中進行了略微調整以適應頁面寬度,並且刪除了一些細節。匹配各個 Java 類的模式定義的開始標記用粗體顯示以強調結構。

清單 2. 生成的模式

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:tns="http://jibx.org/starter" elementFormDefault="qualified"
  targetNamespace="http://jibx.org/starter">
 <xs:complexType name="address">
  <xs:annotation>
   <xs:documentation>Address information.</xs:documentation>
  </xs:annotation>
  <xs:sequence>
   <xs:element type="xs:string" name="street1" minOccurs="0">
    <xs:annotation>
     <xs:documentation>First line of street information (required).</xs:documentation>
    </xs:annotation>
   </xs:element>
   ...
  </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 name="customer" minOccurs="0">
    <xs:complexType>
     ...
    </xs:complexType>
   </xs:element>
   <xs:element type="tns:address" name="billTo" minOccurs="0">
    <xs:annotation>
     <xs:documentation>Billing address information.</xs:documentation>
    </xs:annotation>
   </xs:element>
   <xs:element name="shipping" minOccurs="0">
    <xs:simpleType>
     <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:restriction>
    </xs:simpleType>
   </xs:element>
   <xs:element type="tns:address" name="shipTo" minOccurs="0">
    <xs:annotation>
     <xs:documentation>Shipping address information. If missing, the billing address is
       also used as the shipping address.</xs:documentation>
    </xs:annotation>
   </xs:element>
   <xs:element name="item" minOccurs="0" maxOccurs="unbounded">
    <xs:complexType>
     <xs:sequence>
      <xs:element type="xs:string" name="id" minOccurs="0">
       <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:element>
      <xs:element type="xs:string" name="description" minOccurs="0">
       <xs:annotation>
        <xs:documentation>Text description of item.</xs:documentation>
       </xs:annotation>
      </xs:element>
     </xs:sequence>
     <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:element>
  </xs:sequence>
  <xs:attribute type="xs:long" use="required" name="orderNumber"/>
  <xs:attribute type="xs:date" name="orderDate">
   <xs:annotation>
    <xs:documentation>Date order was placed with server.</xs:documentation>
   </xs:annotation>
  </xs:attribute>
  <xs:attribute type="xs:date" name="shipDate">
   <xs:annotation>
    <xs:documentation>
     <![CDATA[Date order was shipped. This will be <code>null</code> if the order
       has not yet shipped.]]></xs:documentation>
   </xs:annotation>
  </xs:attribute>
 </xs:complexType>
</xs:schema>

默認情況下,BindGen 將為只使用一次的類型生成帶有嵌套的 complexType 和 simpleType 定義的模式,並為使用多次的類型生成帶有各種定義的模式。在這種情況下,嵌套的樣式將得到只有 3 個全局定義的模式:address 和 order 復雜類型以及 order 元素。Order 類在兩個位置使用了 Address 類(表示帳單地址和送貨地址),因此該類在模式中是由單獨的全局定義表示的(僅當定義為全局定義時,模式才允許您重用定義)。Java 數據結構中的其他類(Customer、Item 和 Shipping)都只在 Order 類中被引用了一次,因此相應的類型定義是直接嵌入到 order 模式類型定義中的。

可以通過輸入類中的 Javadoc 生成模式文檔,這是 BindGen 比較棒的功能之一。在 清單 2 中,您可以看到 清單 1 中帶有 Javadoc 的各個字段的模式文檔,以及與帶有 Javadoc 的類對應的各個全局類型的模式文檔。BindGen 的默認處理並不能使所有形式的 Javadoc 與模式組件相匹配 — 並且某些 Javadoc(例如關於 “get” 訪問方法的 Javadoc)在轉換成模式文檔後可能看上去很奇怪 — 但是得到的模式文檔對於闡明 XML 表示的正確用法極為有用。如果需要在轉換過程中對文本進行某些更改(例如從 “get” 方法 Javadoc 中去掉 “Get the ...” 引導語句),您甚至可以為用作模式文檔的 Javadoc 定義您自己的 formatter 類。

僅當您可以獲得類的源代碼並且為 BindGen 提供一個實參告訴它根目錄路徑時,才可以使用這項 Javadoc 轉換功能。在我先前提供的命令行樣例中,提供的源代碼路徑為 -s src。

生成的 JiBX 綁定

除了模式定義之外,BindGen 還將生成 JiBX 綁定定義(在本例中為 binding.xml 文件),該綁定定義將告訴 JiBX 綁定編譯器如何在 Java 類與 XML 之間進行轉換。實際上,該綁定定義是 BindGen 的主要輸出,同時通過綁定生成了模式。綁定定義包含 JiBX 所完成的轉換的完整信息,因此它們必然非常復雜。幸運的是,使用 BindGen 綁定和模式生成,您無需了解綁定定義即可使用 JiBX,因此本教程不會介紹這部分詳細信息。

處理 XML 文檔

在本節中,您將了解如何在運行時運行 JiBX 綁定編譯器和使用 JiBX,從而處理 XML 文檔。

運行 JiBX 綁定編譯器

要在處理 XML 文檔時使用生成的綁定定義,首先需要運行 JiBX 綁定編譯器工具。按照綁定定義的指定,綁定編譯器將把字節碼添加到編譯後的類文件,這些文件實際實現了與 XML 之間的來回轉換。每次重新編譯 Java 類或修改綁定定義時,都必須運行綁定編譯器,因此一般最好把綁定編譯器步驟添加到項目的標准構建流程中。

jibx-bind.jar 中的 JiBX 發行版附帶了綁定編譯器。JiBX 文檔將提供通過各種方法運行綁定編譯器的完整信息,包括如何在運行時而非在構建時運行綁定編譯器。JiBX 還提供了 Eclipse 和 IntelliJ IDEA 的插件,這樣在使用這些 IDE 時將自動運行綁定編譯器。

根據本教程的目的,您將把一切簡單化並且只通過 Ant 使用 build.xml 的 bind 目標運行綁定編譯器。假定您已經運行了 compile 和 bindgen 目標,圖 2 將顯示運行此目標時應當會看到的輸出(您還可以通過在命令行中按順序列出這些目標來運行全部三個目標:ant compile bindgen bind)。

圖 2. Ant 構建 bind 任務

在運行時使用 JiBX

清單 3 顯示了匹配生成模式的簡單測試文檔,包含在教程的代碼下載中,名為 data.xml:

清單 3. 默認綁定測試文檔

<order orderNumber="12345678" orderDate="2008-10-18" shipDate="2008-10-22"
    xmlns="http://jibx.org/starter">
  <customer customerNumber="5678">
    <firstName>John</firstName>
    <lastName>Smith</lastName>
  </customer>
  <billTo>
    <street1>12345 Happy Lane</street1>
    <city>Plunk</city>
    <state>WA</state>
    <postCode>98059</postCode>
    <country>USA</country>
  </billTo>
  <shipping>PRIORITY_MAIL</shipping>
  <shipTo>
    <street1>333 River Avenue</street1>
    <city>Kirkland</city>
    <state>WA</state>
    <postCode>98034</postCode>
    <country>USA</country>
  </shipTo>
  <item quantity="1" price="5.99">
    <id>AC4983498512</id>
    <description>Left-handed widget</description>
  </item>
  <item quantity="2" price="9.50">
    <id>IW2349050499</id>
    <description>Right-handed widget</description>
  </item>
  <item quantity="1" price="8.95">
    <id>RC3000488209</id>
    <description>High-speed MP3 rewinder</description>
  </item>
</order>

下載包還包括一個簡單測試程序,它在本文中顯示為 清單 4,用於演示如何使用 JiBX 解組 及編組 文檔(編組是在內存中生成對象的 XML 表示的過程,可能包括從初始對象鏈接的對象;解組是編組的反向過程,它將通過 XML 表示在內存中構建一個對象(還有可能是一些鏈接的對象)。Ant run 目標將執行此測試程序,使用 清單 3 文檔作為輸入並把編組後的文檔副本寫到名為 out.xml 的文件中。

清單 4. 測試程序

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);
        }
    }
}

圖 3 顯示了運行 run 目標時應當會看到的輸出:

圖 3. Ant 構建 run 任務

您可以檢查生成的 out.xml 文件,查看它與 清單 3 所示的初始輸入之間的匹配程度。除了名稱空間聲明和屬性順序以及輸出中添加的 total 屬性(由測試程序計算和設置)之外,兩個文檔應當完全相同。情況不會永遠是這樣!像大多數形式的數據綁定一樣,JiBX 只處理文檔中的 “重要” 數據(意味著應用程序所使用的那些值)。在解組文檔時,文檔的不重要部分(例如開始或結束標記內的空白,元素之間的文本及注釋)將丟失。在本例中,輸入文檔和輸出文檔之所以如此相似,部分原因是 清單 4 中的代碼把輸出格式設為在每個元素嵌套級別使用兩個空格的縮進,這與輸入文檔相匹配。

細心的讀者會注意到,在輸出文檔的項目清單部分(如清單 5 所示)中,輸入與輸出之間似乎 存在一個很大的差異:

清單 5. 輸出文檔的項目清單

<item quantity="1" price="5.99">
    <id>AC4983498512</id>
    <description>Left-handed widget</description>
  </item>
  <item quantity="2" price="9.5">
    <id>IW2349050499</id>
    <description>Right-handed widget</description>
  </item>
  <item quantity="1" price="8.95">
    <id>RC3000488209</id>
    <description>High-speed MP3 rewinder</description>
  </item>

如果將 清單 5 中用粗體顯示的行與 清單 3 初始文檔中的對應行相比較,您可以看到價格已經從 9.50 變為 9.5,小數點最後位的零被去掉了。可是,這並不是錯誤。價格值使用的表示是 float,並且根據 Java 和 XML 模式,小數點前面的零和後面的零對於 float 來說並不重要。

在本節中,您將了解如何自定義 BindGen 操作以控制數據的 XML 表示、更改名稱及名稱空間的樣式以及控制模式結構的某些方面。

自定義 BindGen 操作

BindGen 支持對綁定和模式生成的各個方面進行大量自定義。要應用的自定義集將作為 XML 文檔傳遞給 BindGen,該文檔中擁有與 Java 代碼結構對應的嵌套元素。清單 6 給出了簡單示例:

清單 6. 簡單的自定義示例

<custom>
  <package name="org.jibx.starter" property-access="true">
    <class name="Address" includes="street1 street2 city state postCode country"/>
    <class name="Item" excludes="description"/>
  </package>
</custom>

本例將處理單個 Java 代碼包,因此 清單 6 僅使用 <custom> 根元素的一個 <package> 子元素。<package> 和 <class> 自定義元素將使用與所有被包含的 <package> 元素相關的名稱屬性,因此在 清單 6 示例中,每個 <class> 元素僅需要一個簡單的類名。<package> 元素可以相互嵌套,因此如果跨越包的層次結構處理類,則使用嵌套的 <package> 元素處理所有選項將十分輕松。嵌套結構尤為便利,因為正如我稍後將在本節中討論的那樣,許多自定義屬性都是通過元素嵌套繼承的。可是,使用嵌套是可選的 — 如果需要,您也可以完全跳過 <package> 元素,並直接使用擁有完全限定類名的 <class> 元素。

使用 -c file-path 命令格式,把自定義文件作為命令行參數傳遞給 BindGen。自定義永遠是可選的,並且您永遠不需要使用自定義文件,除非需要更改默認的 BindGen 行為。

控制 BindGen 使用代碼的方式

BindGen 通過對 Java 類的默認處理可以完成某種程度的工作,但是在沒有用戶指導的情況下,可以完成的工作是有限的。例如,默認處理是在 XML 表示中包括除了 static、transient 或 final 字段以外的所有字段。使用這種方法對於表示簡單數據對象的類來說沒有問題;但是,如果類包括狀態信息或計算值,則最終的 XML 表示可能包括不想公開到類之外的值。

自定義允許您通過兩種方式控制 BindGen 在 XML 表示中使用的內容。首先,您可以輕松地使用 bean 樣式的 getXXX()、setXXX() 和 isXXX() 訪問方法,而不是直接訪問字段。其次,您可以選擇是列出需要在類的 XML 表示中包括的值,還是列出需要排除的值。清單 6 中的自定義演示了這兩個技巧。

單向轉換

本教程使用 JiBX 進行 Java 數據結構與 XML 文檔之間的雙向轉換。BindGen 還支持生成單向轉換,在某些情況下生成單向轉換可能會更方便。例如,值對象類通常是不可修改的,並且只定義 final 字段和讀取(“get”)訪問方法。這使得值對象類很難用於 BindGen 的雙向轉換。如果改為告訴 BindGen 生成僅執行輸出的轉換,它將輕松地處理您需要的字段或屬性。要生成單向轉換,請在自定義的根 <custom> 元素中使用 direction="output" 或 direction="input"。

清單 6 中的 <package> 元素將使用 property-access="true" 屬性來告訴 BindGen,當確定要在 XML 表示中包含哪些值時,查找公共的非靜態訪問方法定義的 bean 樣式的屬性,而不是查找字段定義的 bean 樣式的屬性。此屬性是繼承自定義設置的一個例子,繼承的自定義設置將應用到帶有該屬性的元素內嵌套的所有內容中。在 清單 6 示例中,設置將應用到兩個嵌套的 <class> 元素中。它還將應用到包中的所有其他類中,即使其他類中沒有 <class> 自定義元素。除了確定如何從類表示中找到值之外,property-access 設置還將控制如何通過生成的 JiBX 代碼訪問值 — 直接通過字段還是通過調用訪問方法。

清單 6 中的第一個 <class> 元素將使用 includes="street1 street2 city state postCode country" 屬性列出 BindGen 需要包含在 XML 表示中的類的特定值。清單中的第二個 <class> 元素將使用 excludes="description" 屬性,該屬性將列出要從 XML 表示中排除的值。由於對於值使用的是屬性訪問而非字段訪問,因此這些名稱與 get/set/is 訪問方法所定義的屬性相匹配。如果使用字段,則值名稱將與字段名稱相匹配。

custgen1 Ant 目標將使用 清單 6 中的自定義操作運行 BindGen。此目標是先前所示的 bindgen 目標的替代目標,因此要運行完整的構建,您最好使用 Ant 命令行:ant compile custgen1 bind。清單 7 顯示了在此目標運行時生成的模式中的項目類型定義:

清單 7. 自定義模式輸出片段

<xs:element name="item" minOccurs="0" maxOccurs="unbounded">
  <xs:complexType>
    <xs:sequence>
      <xs:element type="xs:string" name="id" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute type="xs:int" use="required" name="quantity"/>
    <xs:attribute type="xs:float" use="required" name="price"/>
  </xs:complexType>
</xs:element>

您可以從 清單 7 中看出,模式表示現在缺少了描述值,正如自定義指定的那樣。

在使用此自定義時,您需要使用其他 XML 文檔作為輸入,因為不再使用初始 XML 中提供的 <description> 元素。Ant run1 目標將運行使用 data1.xml 作為輸入並生成 out1.xml 作為輸出的測試程序。您還可以用 custom1 Ant 目標運行從編譯源代碼到運行測試程序的整個過程。

控制實例創建

還可以使用自定義控制解組期間的實例創建。默認情況下,JiBX 期望為每個類定義無實參(默認)的構造函數(Java 編譯器將自動生成這些構造函數,如果您不定義任何其他構造函數的話)。在解組過程中需要類的新實例時,JiBX 將使用無實參構造函數創建實例。如果您的某些類只定義帶有實參的構造函數,則可以使用 BindGen 自定義,將這些構造函數變成 JiBX 可以使用的構造函數。實現此目的一種方法是定義創建類實例時使用的工廠方法,其中在 <class> 自定義元素中使用 factory="xxx" 屬性,以提供返回類實例的靜態方法的完全限定名稱(帶有前導包和類信息)。您還可以在根 <custom> 元素中添加 add-constructors="true",它將生成一個根據需要向類中添加無實參構造函數的綁定。這第二種方法對於普通數據類是起作用的,但是您仍然需要提供所有實例或抽象類的工廠(因為它是絕不可以直接構造的)。當然,如果要生成僅執行輸出的綁定,則實例創建不成問題,並且您不需要考慮構造函數。

處理輸入類的其他自定義

BindGen 支持許多其他類型的自定義,用於控制 BindGen 處理 Java 輸入類的方式。例如,如果對 Java 字段名使用命名約定,則可以通過使用 strip-prefixes 或 strip-suffixes 屬性將 BindGen 配置為忽略特殊的前綴或後綴字符串(例如,如果要忽略前導的 m_ 和 s_ 前綴,則需使用 strip-prefixes="m_ s_")。這些對字段名的修改需要在將字段匹配到其他自定義中使用的值名稱之前應用,並且在通過字段名生成 XML 名稱時也將應用這些修改。

您還可以使用嵌套的 <value> 元素自定義類中各個字段或 bean 屬性的處理方式。您將在後面的示例中看到如何使用這些值自定義元素。

控制 XML 表示

除了控制 BindGen 解析 Java 代碼的方法之外,您還可以使用自定義控制數據的 XML 表示。無論值是可選的還是必要的,這些 XML 自定義都包括值的實際表示(作為元素或屬性)、元素和屬性的順序和名稱等等。

前面 清單 6 中的自定義示例演示了對於第一個 <class> 元素使用的 includes="street1 street2 city state postCode country" 屬性形式的一個 XML 自定義。我討論了此自定義如何從類中選擇包含在 XML 表示中的值。它還將控制 XML 表示,因為列出值的順序將成為它們在 XML 表示中的順序。這對於在 XML 中總是被視為無序的屬性來說不是重要的問題,但是對於元素來說十分重要。

如果沒有 在 <class> 自定義中使用 includes 屬性指定值的順序,BindGen 將對類使用 Java 反射,按照值的分發順序生成值。對於大多數 Java 編譯器和 JVM,此反射順序將與 Java 源代碼中的定義順序將匹配。但是,Java 編譯器和 JVM 不是必須 要保留來自源代碼的這個順序,因此某些編譯器或 JVM 可能導致 BindGen 更改子元素的順序。如果您希望在無論使用哪個 Java 編譯器和 JVM 的情況下都確保 XML 表示始終相同,使用 includes 屬性可以幫助您輕松地固定順序。

您還可以使用 includes 屬性控制值的 XML 表示。BindGen 允許在列表中的各個名稱上使用前導標志字符以指名表示:@ 表示屬性,/ 表示屬性。因此如果把 清單 6 的自定義更改為 includes="street1 street2 city state @postCode country",則郵政編碼值的表示將從子元素變為屬性。

控制必需狀態

另一項輕松的自定義是使用 <class> 元素的 requireds 和 optionals 屬性,可以控制是否將值設為可選值,還是設為必需值。和使用 includes 屬性一樣,您可以在 requireds 和 optionals 列表中的名稱前面加一個標志字符,表示是將它們設為子元素,還是設為屬性。

默認情況下,BindGen 將把所有原語值和簡單對象值(帶有直接的 XML 等效類型而非 String 的類)處理為屬性,並把所有復雜對象值處理為子元素。所有原語值都將被處理為必需值,而所有對象值都將被處理為可選值。除了通過使用 includes、requireds 和 optionals 元素在 <class> 自定義級別覆蓋這些默認值之外,您還可以通過在任意自定義級別(<custom>、<package> 或 <class> 元素)中設置 value-style="element" 屬性,將默認表示改為對所有值使用元素。您還可以使用 require 屬性來控制:在 XML 中,哪些類型應當被處理為必需值:

require="none" 將把??有內容都處理為可選值。

require="primitives" 是默認值,僅將原語值處理為必需值。

require="objects" 與默認值相反,將把原語值設為可選值,將對象類型處理為必需值。

require="all" 將把所有值默認處理為必需值。

清單 8 顯示了教程下載部分的 dwcode1 目錄中的 custom2.xml 自定義文件,演示了我在本節中討論過的幾項功能:

清單 8. 自定義順序、必需狀態及表示

<custom property-access="true">
  <package name="org.jibx.starter">
    <class name="Address" includes="street1 street2 city @state @postCode country"
        requireds="street1 city"/>
    <class name="Customer" includes="customerNumber firstName lastName"
        requireds="lastName firstName /customerNumber"/>
    <class name="Item" excludes="description" requireds="@id quantity price"/>
    <class name="Order" requireds="/orderNumber customer billTo shipping orderDate"/>
  </package>
</custom>

您可以通過使用 Ant custgen2 目標(ant compile custgen2 bind,用於運行完整的構建)嘗試這組自定義。清單 9 顯示了使用這些自定義的生成模式的一部分,顯示了結果順序、必需狀態(minOccurs="0" 在模式中被默認為是必需的,表示可選的元素,use="required" 在模式中被默認為是可選的,表示必需的屬性)和元素或屬性表示:

清單 9. 使用自定義生成的模式

<xs:complexType name="order">
  <xs:annotation>
    <xs:documentation>Order information.</xs:documentation>
  </xs:annotation>
  <xs:sequence>
    <xs:element type="xs:long" name="orderNumber">
      <xs:annotation>
        <xs:documentation>Get the order number.</xs:documentation>
      </xs:annotation>
    </xs:element>
    <xs:element name="customer">
      <xs:complexType>
        <xs:sequence>
          <xs:element type="xs:long" name="customerNumber"/>
          <xs:element type="xs:string" name="firstName"/>
          <xs:element type="xs:string" name="lastName"/>
        </xs:sequence>
      </xs:complexType>
    </xs:element>
    ...
  </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>
<xs:element type="tns:order" name="order"/>
<xs:complexType name="address">
  <xs:annotation>
    <xs:documentation>Address information.</xs:documentation>
  </xs:annotation>
  <xs:sequence>
    <xs:element type="xs:string" name="street1"/>
    <xs:element type="xs:string" name="street2" minOccurs="0"/>
    <xs:element type="xs:string" name="city"/>
    <xs:element type="xs:string" name="country" minOccurs="0"/>
  </xs:sequence>
  <xs:attribute type="xs:string" name="state"/>
  <xs:attribute type="xs:string" name="postCode"/>
</xs:complexType>

在使用 bind Ant 任務編譯綁定之後,您可以使用以 data2.xml 測試文檔為輸入並生成輸出 out2.xml 的 run2 任務測試此模式。您還可以從編譯開始運行整個過程以用 custom2 目標進行測試。清單 10 顯示了測試文檔:

清單 10. 匹配自定義的測試文檔

<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="8394983498512"/>
  <item quantity="2" price="9.50" id="9912349050499"/>
  <item quantity="1" price="8.95" id="1293000488209"/>
</order>

比較 清單 10 與 清單 3 所示的初始測試文檔,看一看自定義如何改變數據的 XML 表示(包括將排列項表示改為空元素,這是一種比原來更緊湊的表示)。

控制名稱和名稱空間

Java 名稱通常使用 “大小寫混合" 樣式:名稱大部分是小寫的,但是每個單詞的首字母是大寫的。對於字段或屬性名稱,首字母大寫只適用於第一個單詞之後的單詞(得到諸如 postCode 和 customerNumber 之類的名稱)。XML 名稱不是標准化的,並且通常使用幾種不同的樣式。這些樣式包括首字母小寫的大小寫混合樣式(Java 字段和屬性名稱樣式)、首字母大寫的大小寫混合(Java 類名樣式)、連字符分隔符(用連字符分隔單詞)樣式、點分隔符(用點分隔單詞)樣式以及下劃線分隔符(用下劃線分隔單詞)樣式。

默認情況下,BindGen 對 XML 名稱采取大小寫混合樣式,但是您可以通過在任意自定義級別中設置 name-style 屬性(<custom>、<package> 或 <class> 元素)輕松地更改此樣式。此屬性允許的值與上面列出的各種 XML 樣式相匹配:

camel-case(默認)

upper-camel-case

hyphenated

dotted

underscored

您還可以使用專門針對值的自定義設置該值的 XML 名稱。使用單獨的值自定義將使您可以完全控制該值的訪問方法及在 XML 中的表示方法。基於您已經在前面示例中看到的相同示例代碼,清單 11 將給出若干個將自定義元素用於單獨值的示例:

清單 11. 自定義名稱和名稱空間

<custom property-access="true" name-style="hyphenated" namespace="http://jibx.org/custom"
    namespace-style="fixed">
  <package name="org.jibx.starter">
    <class name="Address" includes="street1 street2 city @state @postCode country"
        requireds="street1 city"/>
    <class name="Customer" includes="customerNumber firstName lastName"
        requireds="lastName firstName /customerNumber"/>
    <class name="Item" excludes="description" requireds="@id quantity price"/>
    <class name="Order" requireds="orderNumber customer billTo shipping orderDate">
     <value property-name="orderNumber" element="order-num"/>
      <value property-name="items" item-name="line-item" element="order-items"/>
    </class>
  </package>
</custom>

清單 11 中的第一個值自定義用於 <class name="Order"...> 元素中的 orderNumber 屬性。通過使用 element="order-num" 屬性,orderNumber 自定義將告訴 BindGen 將值表示為元素,而不是原語值使用的默認屬性形式。第二個自定義用於 items 集合屬性。此自定義將使用 item-name 和 element 屬性。item-name 屬性將控制集合所表示的各個值使用的名稱,而 element 屬性將強制使用提供的名稱作為集合中的值的封裝元素。

沒有名稱空間的 XML

所有教程示例都使用 XML 名稱空間,因為通常把使用名稱空間視為數據交換的最佳實踐。如果需要使用沒有名稱空間的 XML,您可以在任意自定義級別使用 namespace-style="none" 屬性徹底關閉所有嵌套組件的名稱空間。

清單 11 中的自定義還定義要在 XML 文檔中使用的名稱空間。前面的示例依賴於 BindGen 對名稱空間的默認處理:從 Java 包派生出在 Java 代碼的 XML 表示中使用的名稱空間 URI。此默認處理把 org.jibx.starter 包轉換成了名稱空間 URI http://jibx.org/starter。在 清單 11 中,名稱空間是通過在根 <custom> 元素中添加一對屬性 — namespace="http://jibx.org/custom" 和 namespace-style="fixed" — 來自定義的。這些屬性中的第一個屬性將定義基本名稱空間,而第二個屬性將防止根據 Java 包修改名稱空間的一般行為。這些屬性都是通過嵌套自定義元素繼承的,因此可以將其輕松地放到 <package> 元素中,而不是放在 <custom> 元素中。

您可以通過使用 Ant custgen3 目標生成綁定和模式,並使用 run3 目標運行測試(在使用標准 bind 目標運行 JiBX 綁定編譯器後 — 或者使用 full3 目標執行整個過程),嘗試執行 清單 11 中的自定義。清單 12 顯示了用於測試代碼的輸入文檔:

清單 12. 帶有自定義名稱及名稱空間的 XML 樣例

<order order-date="2008-10-18" ship-date="2008-10-22" xmlns="http://jibx.org/custom">
<order-num>12345678</order-num>
<customer>
<customer-number>5678</customer-number>
<first-name>John</first-name>
<last-name>Smith</last-name>
</customer>
<bill-to state="WA" post-code="98059">
<street1>12345 Happy Lane</street1>
<city>Plunk</city>
<country>USA</country>
</bill-to>
<shipping>PRIORITY_MAIL</shipping>
<ship-to state="WA" postCode="98034">
<street1>333 River Avenue</street1>
<city>Kirkland</city>
</ship-to>
<order-items>
<line-item quantity="1" price="5.99" id="AC4983498512"/>
<line-item quantity="2" price="9.50" id="IW2349050499"/>
<line-item quantity="1" price="8.95" id="RC3000488209"/>
</order-items>
</order>

如果比較 清單 12 與 清單 10 中的樣例,您將看到最新的自定義如何更改了表示。

自定義模式表示

現在,您已經了解了 BindGen 自定義如何更改 Java 數據的 XML 表示。自定義還可用於控制實際模式結構的某些方面。

回想一下,BindGen 默認對全局類型和元素優先使用嵌套定義。如果回顧 清單 9 生成的模式,您將看到此嵌套結構。模式僅使用三個全局定義:address 和 order 復雜類型以及 order 元素。Java 數據結構中的其他類(Customer、Item 和 Shipping)都只在 Order 類中引用一次,因此相應的類型定義是直接嵌入在 order 模式類型定義中的。

您可以在任意嵌套自定義元素中使用 force-mapping="true" 屬性來更改模式樣式。清單 13 顯示了 custom4.xml 自定義文件,該文件將把此更改添加到匹配 清單 9 生成的模式的 custom2.xml 自定義中:

清單 13. 模式結構的自定義

<custom property-access="true" force-mapping="true">
<package name="org.jibx.starter">
<class name="Address" includes="street1 street2 city @state @postCode country"
requireds="street1 city"/>
<class name="Customer" includes="customerNumber firstName lastName"
requireds="lastName firstName /customerNumber"/>
<class name="Item" excludes="description" requireds="@id quantity price"/>
<class name="Order" requireds="/orderNumber customer billTo shipping orderDate"/>
</package>
</custom>

清單 14 顯示了得到的模式結構(通過運行 custgen4 Ant 目標生成為 starter.xsd)。此版本的模式表示的 XML 文檔結構與 清單 9 模式相同,但是包括匹配每個 Java 類的單獨類型定義。

清單 14. 自定義的模式結構

<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: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:attribute type="xs:int" use="required" name="quantity"/>
<xs:attribute type="xs:float" use="required" name="price"/>
</xs:complexType>
<xs:element type="tns:order" name="order"/>
<xs:complexType name="address">
<xs:annotation>
<xs:documentation>Address information.</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element type="xs:string" name="street1"/>
<xs:element type="xs:string" name="street2" minOccurs="0"/>
<xs:element type="xs:string" name="city"/>
<xs:element type="xs:string" name="country" minOccurs="0"/>
</xs:sequence>
<xs:attribute type="xs:string" name="state"/>
<xs:attribute type="xs:string" name="postCode"/>
</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:element type="xs:string" name="firstName"/>
<xs:element type="xs:string" name="lastName"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="order">
<xs:annotation>
<xs:documentation>Order information.</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element type="xs:long" name="orderNumber">
<xs:annotation>
<xs:documentation>Get the order number.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element type="tns:customer" name="customer"/>
<xs:element type="tns:address" name="billTo"/>
<xs:element type="tns:shipping" name="shipping"/>
<xs:element type="tns:address" name="shipTo" minOccurs="0"/>
<xs:element type="tns: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>
</xs:schema>

清單 14 所示的類型模式稱為 “Venetian Blind” 樣式模式,常用於復雜的 XML 結構定義。通過分隔各個類型定義,這種模式樣式使您可以在修改或擴展模式時輕松地重用組件結構。如果只是計劃使用 Java 代碼作為進一步更改的基礎(每次代碼更改時都要再次運行 BindGen),則 Venetian Blind 樣式的靈活性可能並不重要,但是如果想要使用模式作為以後開發的基礎,則非常適合使用 Venetian Blind。

BindGen 命令行參數

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

表 1. BuildGen 命令行選項

命令 用途 -b name 生成根綁定定義文件名(默認名稱為 binding.xml) -c path 輸入自定義文件的路徑 -n uri=name,... 提供模式名稱空間 URI 和文件名對(默認通過模式名稱空間 URI 生成文件名) -p path,... 載入 Java 類文件的路徑(默認值為運行 BindGen 所使用的類路徑) -s path,... 載入 Java 源文件的路徑(默認情況下不使用源代碼) -t path 生成輸出的目標目錄路徑(默認值為當前目錄) -w 在生成輸出前從目標目錄中刪除所有文件(如果目標目錄就是當前目錄,則忽略)

通過使用 -- 作為自定義屬性值的特殊前綴,您還可以將全局定義作為命令行參數傳遞給 BindGen,而無需創建自定義文件。因此要設置 清單 13 自定義中所使用的全局選項,您需要向 BindGen 命令行中添加 --property-access=true --force-mapping=true。在使用這項技巧時,不需要對屬性值使用引號。如果需要設置獲取多個值的列表的自定義,只需使用逗號而不要使用空格作為各個值之間的分隔符(這樣可以忽略字段名中的前綴 m_ 和 s_,例如,使用命令行參數 --strip-prefixes=m_,s_)。

結束語

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

通過使用實際的綁定定義,您可以進一步使用 JiBX,這將使您可以控制轉換過程的每一個步驟。使用綁定定義中構建的用戶擴展鉤(hook),您可以輕松地將自己的代碼添加到轉換過程中,作為其中的一部分執行。您甚至可以創建自己的自定義編組和解組代碼,這些代碼可以有選擇性地從 JiBX 所生成的代碼處接管控制權,以處理獨特的 XML 或 Java 數據結構。JiBX 文檔包括演示使用綁定定義的各個方面的教程,包括這些擴展功能,以及有關所有詳細信息的參考文檔。

當您需要快速地為數據交換開發模式定義,而不希望了解有關模式的大量信息時,使用 JiBX 尤為方便。XML Schema 標准十分復雜,並且用於處理模式定義的工具為重構模式提供的支持很少。如本教程所示,通過使用 Java 代碼和 BindGen 作為模式開發的基礎,您可以應用 Java IDE 的全部靈活性快速而輕松地創建模式定義,而無需費心研究如何使用 JiBX。

JiBX 還提供了一個基於 Java 代碼為 Web 服務生成完整 WSDL 和模式定義的工具。此工具名為 Jibx2Wsdl,構建在 BindGen 之上。您可以將本文中討論的所有 BindGen 自定義用於數據類,作為您自己的服務方法的輸入和輸出,這樣生成的模式將反映您的偏好。JiBX 文檔將提供如何使用 Jibx2Wsdl 的詳細信息。

在 第 2 部分 中,您將了解如何使用 JiBX 通過 XML 模式定義生成 Java 代碼。

本文配套源碼

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