在你开始前
关于本教程
JiBX是用于将XML数据绑定到Java对象的工具。 长期以来,JiBX数据绑定是将Java代码绑定到XML的最快,最灵活的方法。 但是,其绑定定义的复杂性以及对日益广泛使用的XML模式定义的有限支持有时使用户感到沮丧。 幸运的是,JiBX的1.2版本对于消除这些问题大有帮助。 在本教程中,您将学习如何使用JiBX 1.2的新功能从现有Java代码轻松生成XML模式定义,以及读写与生成的模式定义匹配的XML文档-所有这些都无需深入了解JiBX绑定的细节。定义。 第2部分介绍了从XML模式定义开始到生成Java代码的另一面。
目标
本教程将指导您完成使用JiBX从Java代码生成XML模式定义的过程。 首先,您将了解如何从简单的Java数据模型开始,以及如何生成与该模型匹配的默认模式。 在此基础上,您将学习如何轻松地应用一系列自定义项来控制Java类中使用的实际值以及如何访问它们,无论它们是必需的还是可选的,以及XML中使用的名称和名称空间,以及甚至生成的模式定义的结构。 在此过程中,您将看到JiBX如何通过利用对Javadocs的投资自动为架构定义提供文档,从而为生成的架构增加价值。 阅读完本教程并完成提供的示例后,您将能够使用JiBX从您自己的Java数据结构类生成高质量的XML模式定义。
先决条件
要理解本教程,您至少应具有Java代码和XML的基本知识。 您不需要详细了解XML模式定义,但是对模式的一些熟悉将有助于您更好地理解示例。
系统要求
要运行示例,您需要安装:
- 要么:
- Sun的JDK 1.5.0_09 (或更高版本)。
- IBM Java技术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文件的形式提供。 在系统上安装它的最简单方法是将ZIP扩展到JiBX发行版的根目录中(或在Windows®上,将zip文件中的dwcode1目录从ZIP文件内部复制到JiBX发行版的根目录中)。 这应该在jibx目录中创建一个dwcode1子目录,并在该dwcode1子目录内创建示例文件(包括build.xml,custom1.xml等)。
该示例代码包含一个Apache Ant构建文件,以自动运行JiBX工具并处理示例中涉及的其他步骤。 如果将示例代码直接安装到JiBX安装目录中,则该构建无需任何其他配置即可访问JiBX JAR。 如果将示例代码安装在其他位置,则仍然可以使用Ant构建。 在这种情况下,您只需要将JIBX_HOME
环境变量设置为您的JiBX安装路径。 或者,您可以编辑sample-code目录中的build.xml文件,并取消注释直接设置jibx-home
属性的文件顶部附近的行。
从代码生成默认绑定和架构
很容易从Java代码生成JiBX绑定定义以及相应的XML模式定义。 您将在本节中学习如何操作。
介绍Java示例代码
作为示例,我将从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®系统上,Java命令行(由单行组成,即使它看起来像包装在您的显示器中一样)也可以从dwcode1目录中的控制台复制Ant bindgen
目标(假设您遵循了推荐的安装说明)是:
java -cp ../lib/jibx-tools.jar:bin org.jibx.binding.generator.BindGen
-s src org.jibx.starter.Order
在Windows上,命令(单行显示,而不管其外观如何)为:
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
定义,以及用于多次使用的类型的单独定义。 在这种情况下,嵌套样式将导致仅具有三个全局定义的架构: address
和order
复杂类型以及order
元素。 Address
类在Order
类中的两个地方(用于帐单和收货地址)使用,这就是为什么该类在模式中由单独的全局定义表示的原因。 (使用Schema,仅当它们是全局的时,您才可以重用定义。)Java数据结构中的其他类( Customer
, Item
和Shipping
)每个都仅在Order
类中的一点被引用,因此嵌入了相应的类型定义。直接在order
模式类型定义内。
BindGen更好的功能之一是它可以从输入类中的Javadocs生成模式文档。 你可以看到在架构文档清单2的每个字段与一个javadoc 清单1 ,以及对应于具有的Javadoc类每个全局类型。 BindGen的默认处理方式并非所有形式的Javadocs都可以与模式组件匹配-某些Javadocs(例如那些使用“ get”访问方法的Javadocs)在转换为模式文档时可能看起来很奇怪-但是生成的模式文档对于阐明这些信息非常有用。正确使用XML表示形式。 如果要在转换过程中对文本进行一些更改,甚至可以为用作架构文档的Javadocs定义自己的格式化程序类(例如,从“ get”方法Javadocs中剥离“ Get the ...”前导句子) 。
仅当您具有可用于这些类的源代码并为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中工作时,它们会自动运行绑定编译器。
出于本教程的目的,您将使事情保持简单,并使用build.xml的bind
目标通过Ant运行绑定编译器。 图2显示了运行此目标时应看到的输出,假设您已经运行了compile
和bindgen
目标。 (您还可以通过在命令行上按顺序列出所有三个目标来依次运行它们: 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代码将输出格式设置为对每个元素嵌套级别使用两个空格的缩进,以匹配输入文档。
敏锐的观察者会在输出文档的item-list部分中注意到输入和输出之间似乎很重要的差异,如清单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定制
在本节中,您将学习如何自定义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表示形式中包括除静态,瞬态或最终字段之外的每个字段。 这种方法对于表示简单数据对象的类很好用。 但是,如果您的类包含状态信息或计算值,则最终可能会得到XML表示,其中包含了您不希望在类外公开的值。
自定义允许您以两种方式控制BindGen在XML表示中使用的内容。 首先,您可以轻松地切换到使用bean风格的getXXX()
, setXXX()
和isXXX()
访问方法,而不是直接访问字段。 其次,您可以选择列出要包含在类的XML表示中的值,也可以列出要排除的值。 清单6的定制展示了这两种技术。
清单6中的<package>
元素使用property-access="true"
属性来告诉BindGen在确定将哪些值包括在XML表示形式中时,查找由公共非静态访问方法(而不是字段)定义的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支持许多其他自定义项,用于控制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将所有原始值和简单对象值(具有String
以外的直接XML等效类型的类)视为属性,并将所有复杂对象值视为子元素。 所有原始值都被视为必需,所有对象值均被视为可选。 除了通过使用includes
, requireds
和optionals
元素在<class>
定制级别覆盖这些默认值之外,还可以通过在任意级别设置value-style="element"
属性来更改默认表示形式以将元素用于所有值自定义项( <custom>
, <package>
或<class>
元素)。 您还可以使用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任务编译绑定之后,可以使用run2
任务对其进行测试,该任务将data2.xml测试文档作为输入并生成输出out2.xml。 您还可以运行完整的序列,从编译到使用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名称的驼峰样式,但是您可以通过在任何自定义级别( <custom>
, <package>
或<class>
元素)设置name-style
属性来轻松更改此name-style
。 此属性允许的值与上面列出的不同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
是针对items
集合属性的。 此定制同时使用item-name
和element
属性。 item-name
属性控制用于该集合表示的各个值的名称,而element
属性则强制使用提供的名称作为该集合中的值周围的包装元素。
清单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"
)来<custom>
namespace-style="fixed"
。 这些属性中的第一个定义基本名称空间,而第二个属性则防止基于Java软件包修改名称空间的正常行为。 这些属性都是通过嵌套自定义元素来继承的,因此可以很容易地将它们放置在<package>
元素而不是<custom>
元素上。
You can try out the Listing 11 customizations by using the Ant custgen3
target for the binding and schema generation, and the run3
target to run a test (after using the standard bind
target to run the JiBX binding compiler — or just use the full3
target to do the whole sequence). Listing 12 shows the input document used with the test code:
Listing 12. XML sample with customized names and namespace
<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>
If you compare Listing 12 with the Listing 10 sample, you'll see how the representation has been changed by the latest customizations.
Customizing schema representations
You've now seen how BindGen customizations can change the XML representation of your Java data. Customizations can also be used to control some aspects of the actual schema structure.
Recall that BindGen defaults to using nested definitions in preference to global types and elements. If you review the Listing 9 generated schema, you'll see this nesting structure. The schema uses only three global definitions: the address
and order
complex types, and the order
element. The other classes in the Java data structure ( Customer
, Item
, and Shipping
) are each referenced at only one point in the Order
class, so the corresponding type definitions are embedded directly within the order
schema type definition.
You can change the schema style by using a force-mapping="true"
attribute on any of the nesting customization elements. Listing 13 shows the custom4.xml customizations file, which adds this change to the custom2.xml customizations matching the Listing 9 generated schema:
Listing 13. Customization for schema structure
<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>
Listing 14 shows the resulting schema structure (generated as starter.xsd by running the custgen4
Ant target). This version of the schema represents the same XML document structure as the Listing 9 schema but includes separate type definitions matching each Java class.
Listing 14. Customized schema structure
<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>
Schemas of the type shown in Listing 14 , called "Venetian Blind" style schemas, are popular for use with complex XML structure definitions. By separating out each type definition, this schema style lets you easily reuse component structures when modifying or extending a schema. The flexibility of the Venetian Blind style is probably not important if you just plan to use your Java code as the base for any further changes (rerunning BindGen each time your code changes), but it can be nice if you intend to use the schema as a basis for further development.
BindGen command-line parameters
BindGen supports several command-line parameters in addition to those used in the tutorial code. Table 1 lists the most important options:
Table 1. BuildGen command-line options
命令 | 目的 |
---|---|
-b name | Generated root binding definition file name (default name is binding.xml) |
-c path | Path to input customizations file |
-n uri=name,... | Give schema namespace URI and file-name pairs (default generates file names from schema namespace URIs) |
-p path,... | Paths for loading Java class files (default is the classpath used to run BindGen) |
-s path,... | Paths for loading Java source files (source is not used by default) |
-t path | Target directory path for generated output (default is current directory) |
-w | Wipe all files from target directory before generating output (ignored if the target directory is the same as the current directory) |
You can also pass global customizations to BindGen as command-line parameters, without the need to create a customizations file, by using --
as a special prefix to the customization attribute value. So to set the same global options as used in the Listing 13 customizations, you'd add --property-access=true --force-mapping=true
to the BindGen command line. No quotes are needed for the attribute value when you use this technique. If you want to set a customization that takes a list of multiple values, just use commas rather than spaces as separators between the individual values (so to ignore the prefixes m_
and s_
on field names, for instance, you'd use the command line parameter --strip-prefixes=m_,s_
).
Going further
In this tutorial, you learned the basics of using JiBX to generate an XML schema definition from your Java code and then convert documents matching that schema to and from Java data structures. There are many other customizations you can use to control the schema generation, beyond those I've covered in this tutorial. The JiBX documentation provides full details on all these customization options, along with more examples of schema generation from code.
You can go even further with JiBX by working with the actual binding definitions, which give you control over every step of the conversion process. You can easily add your own code to be executed as part of the conversions, using user extension hooks built into the binding definitions. You can even create your own custom marshalling and unmarshalling code that can selectively take over control from the code generated by JiBX to handle unique XML or Java data structures. The JiBX documentation includes a tutorial that illustrates many aspects of working with binding definitions, including these extension features, along with reference documentation for all the details.
JiBX is especially convenient when you want to develop a schema definition quickly for data exchange without needing to learn a lot about schema. The XML Schema standard is complex, and tools for working with schema definitions provide little support for restructuring and refactoring schemas. By using Java code and BindGen as the basis for your schema development as shown in this tutorial, you can apply all the flexibility of Java IDEs to create schema definitions quickly and easily without in any way committing yourself to using JiBX.
JiBX also includes a tool for generating complete WSDL and schema definitions for Web services based on Java code. This tool, named Jibx2Wsdl, builds on top of BindGen. You can use all the BindGen customizations discussed in this article for the data classes used as inputs and outputs for your service methods, so that the generated schema will reflect your preferences. The JiBX documentation provides details on how to use Jibx2Wsdl.
In Part 2 , you'll learn how to use JiBX to generate Java code from XML schema definitions.
翻译自: https://www.ibm.com/developerworks/java/tutorials/j-jibx1/index.html