该Java教程是为JDK 8编写的。本页面描述的示例和实践不利用后续版本中引入的改进,可能使用不再可用的技术。
有关Java SE 9及其后续版本中更新的语言功能的摘要,请参阅Java语言更改。
有关所有JDK版本的新功能、增强功能和已删除或已弃用选项的信息,请参阅JDK发行说明。
下面的部分描述了几个例子,这些例子是基于基本示例中所演示的概念。
本节的目标是说明如何使用自定义绑定声明来自定义JAXB绑定,有两种方式:
与“基本JAXB示例”中的例子不同,该示例侧重于生成基于模式的Java绑定类之前对XML模式进行的自定义。
本节介绍了可以对JAXB绑定和验证方法进行的自定义。有关更多信息,请参见JAXB规范。
在大多数情况下,JAXB绑定编译器生成的默认绑定已经足够使用了。然而,有些情况下,您可能希望修改默认绑定。其中一些情况包括:
本节介绍了一些核心的JAXB自定义概念:
对默认的JAXB绑定进行自定义是通过传递给JAXB绑定编译器的绑定声明的形式实现的。这些绑定声明可以通过以下两种方式之一进行:
对于一些人来说,使用内联自定义更容易,因为您可以在应用它们的模式的上下文中看到您的自定义。相反,使用外部绑定自定义文件使您可以在不修改源模式的情况下自定义JAXB绑定,并且可以轻松地将自定义应用于多个模式文件。
这些类型的自定义将在以下各节中详细描述:
通过在XML模式文件中的内联绑定声明中使用<xsd:appinfo>元素来进行JAXB绑定的自定义。这些<xsd:appinfo>元素嵌入在模式的<xsd:annotation>元素中(xsd:是XML模式命名空间前缀,在W3C的XML Schema Part 1: Structures中定义)。内联自定义的一般形式如下所示:
<xs:annotation>
<xs:appinfo>
<!--
...
绑定声明
...
-->
</xs:appinfo>
</xs:annotation>
自定义在声明的位置应用于模式。例如,对于特定元素级别的声明仅适用于该元素。请注意,在<annotation>和<appinfo>声明标签中必须使用XML模式命名空间前缀。在上面的示例中,使用xs:作为命名空间前缀,因此声明被标记为<xs:annotation>和<xs:appinfo>。
通过使用包含绑定声明的外部文件来进行JAXB绑定的自定义,其一般形式如下所示:
<jxb:bindings schemaLocation = "xs:anyURI">
<jxb:bindings node = "xs:string">*
<!-- 绑定声明 -->
<jxb:bindings>
</jxb:bindings>
例如,在JAXB绑定声明文件中,第一个schemaLocation/node声明指定了模式名称和根模式节点:
<jxb:bindings schemaLocation="po.xsd" node="/xs:schema"> </jxb:bindings>
后续的schemaLocation/node声明,例如在前面的模式示例中命名为ZipCodeType的simpleType元素,采用以下形式:
<jxb:bindings node="//xs:simpleType [@name=’ZipCodeType’]">
绑定自定义文件必须是ASCII文本。文件的名称或扩展名无关紧要;尽管在本章中使用的典型扩展名是.xjb。
包含绑定声明的自定义文件通过以下语法传递给JAXB绑定编译器xjc:
xjc -b file schema
其中,file是绑定自定义文件的名称,schema是要传递给绑定编译器的模式的名称。
您可以有一个单独的绑定文件,其中包含多个模式的自定义,或者您可以将自定义分成多个绑定文件,例如:
xjc schema1.xsd schema2.xsd schema3.xsd \
-b bindings123.xjb
xjc schema1.xsd schema2.xsd schema3.xsd \
-b bindings1.xjb \
-b bindings2.xjb \
-b bindings3.xjb
请注意,模式文件和绑定文件在命令行中的顺序无关紧要;尽管每个绑定自定义文件必须在命令行上的自己的-b开关之前。
有关xjc编译器选项的更多信息,请参见JAXB编译器选项。
外部绑定自定义文件中的绑定声明有一些规则,这些规则不适用于在源模式中内联声明的类似声明:
<jxb:bindings version="1.0"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
类似地,必须使用XPath表示法指定要应用自定义的模式中的单个节点;例如:
<jxb:bindings node="//xs:complexType [@name=’USAddress’]">
在这种情况下,绑定编译器将自定义应用于节点,就好像声明嵌入在节点的<xs:appinfo>元素中一样。
总结这些规则,外部绑定元素<jxb:bindings>只有在以下三种情况下才会被JAXB绑定编译器识别并处理:
默认的JAXB绑定可以在四个不同的层级或范围上进行自定义或覆盖。
下图展示了自定义声明的继承和优先级。具体而言,位于金字塔顶部的声明会继承并替代位于其下方的声明。
四种类型的JAXB绑定声明的语法,XML到Java数据类型绑定声明的语法以及自定义命名空间前缀的描述在以下部分中。
全局范围的自定义使用<globalBindings>声明。全局范围自定义的语法如下:
<globalBindings>
[ collectionType = "collectionType" ]
[ fixedAttributeAsConstantProperty = "true" | "false" | "1" | "0" ]
[ generateIsSetMethod = "true" | "false" | "1" | "0" ]
[ enableFailFastCheck = "true" | "false" | "1" | "0" ]
[ choiceContentProperty = "true" | "false" | "1" | "0" ]
[ underscoreBinding = "asWordSeparator" | "asCharInWord" ]
[ typesafeEnumBase = "typesafeEnumBase" ]
[ typesafeEnumMemberName = "generateName" | "generateError" ]
[ enableJavaNamingConventions = "true" | "false"
| "1" | "0" ]
[ bindingStyle = "elementBinding" | "modelGroupBinding" ]
[ <javaType> ... </javaType> ]*
</globalBindings>
<globalBindings>声明仅在顶级schema元素的annotation元素中有效。在任何给定的模式或绑定声明文件中,只能有一个<globalBindings>声明的实例。如果一个源模式包括或导入第二个源模式,则<globalBindings>声明必须在第一个源模式中声明。
模式范围的自定义使用<schemaBindings>声明。模式范围自定义的语法为:
<schemaBindings>
[ <package> package </package> ]
[ <nameXmlTransform> ... </nameXmlTransform> ]*
</schemaBindings>
<package
[ name = "包名" ]
[ <javadoc> ... </javadoc> ]
</package>
<nameXmlTransform>
[ <typeName
[ suffix="后缀" ]
[ prefix="前缀" ] /> ]
[ <elementName
[ suffix="后缀" ]
[ prefix="前缀" ] /> ]
[ <modelGroupName
[ suffix="后缀" ]
[ prefix="前缀" ] /> ]
[ <anonymousTypeName
[ suffix="后缀" ]
[ prefix="前缀" ] /> ]
</nameXmlTransform>
如上所示,<schemaBinding> 声明包括两个子组件:
<class> 绑定声明使您可以自定义模式元素与 Java 内容接口或 Java Element 接口之间的绑定。可以使用 <class> 声明来自定义:
<class> 自定义的语法如下:
<class
[ name = "类名"]
[ implClass= "实现类" ] >
[ <javadoc> ... </javadoc> ]
</class>
<property> 绑定声明使您可以自定义 XML 模式元素与其在 Java 中的表示形式之间的绑定,作为属性。自定义的范围可以是定义级别或组件级别,取决于指定 <property> 绑定声明的位置。
<property> 自定义的语法如下:
<property
[ name = "属性名"]
[ collectionType = "属性集合类型" ]
[ fixedAttributeAsConstantProperty = "true" |
"false" | "1" | "0" ]
[ generateIsSetMethod = "true" |
"false" | "1" | "0" ]
[ enableFailFastCheck ="true" |
"false" | "1" | "0" ]
[ <baseType> ... </baseType> ]
[ <javadoc> ... </javadoc> ]
</property>
<baseType>
<javaType> ... </javaType>
</baseType>
<javaType> 声明提供了一种定制将 XML 数据类型转换为 Java 数据类型的方式。XML 提供了比 Java 更多的数据类型,因此当默认的 JAXB 绑定无法充分表示您的模式时,<javaType> 声明允许您指定自定义数据类型绑定。
目标 Java 数据类型可以是 Java 内置数据类型或应用程序特定的 Java 数据类型。如果使用应用程序特定的 Java 数据类型作为目标,您的实现还必须提供解析和打印方法进行数据的反序列化和序列化。为此,JAXB 规范支持一个 parseMethod 和一个 printMethod:
如果您希望定义自己的数据类型转换,JAXB 定义了一个静态类 DatatypeConverter,用于帮助解析和打印 XML Schema 内置数据类型的有效词法表示。
<javaType> 自定义的语法如下:
<javaType name= "javaType"
[ xmlType= "xmlType" ]
[ hasNsContext = "true" | "false" ]
[ parseMethod= "parseMethod" ]
[ printMethod= "printMethod" ]>
<javaType>声明可以在以下情况下使用:
有关如何在自定义数据类型转换器类中实现<javaType>声明和DatatypeConverterInterface接口的示例,请参见MyDatatypeConverter Class。
类型安全枚举声明提供了一种本地化的方式来将XML simpleType元素映射到Java typesafe enum类。有两种类型的类型安全枚举声明:
在这两种情况下,对此类型的自定义存在两个主要限制:
<typesafeEnumClass>自定义的语法如下:
<typesafeEnumClass
[ name = "enumClassName" ]
[ <typesafeEnumMember> ... </typesafeEnumMember> ]*
[ <javadoc> enumClassJavadoc </javadoc> ]
</typesafeEnumClass>
<typesafeEnumMember>自定义的语法如下:
<typesafeEnumMember
name = "enumMemberName">
[ value = "enumMemberValue" ]
[ <javadoc> enumMemberJavadoc </javadoc> ]
</typesafeEnumMember>
对于内联注释,<typesafeEnumClass>声明必须在<simpleType>元素的注释元素中指定。<typesafeEnumMember>必须在枚举成员的注释元素中指定。这使得枚举成员可以与枚举类独立自定义。
有关类型安全的枚举设计模式的信息,请参阅Joshua Bloch的《Effective Java Programming》在Oracle Technology Network上的示例章节。
<javadoc>声明允许您向基于模式的JAXB包、类、接口、方法和字段添加自定义的Javadoc工具注释。请注意,<javadoc>声明不能全局应用;它们只在其他绑定自定义的子元素中有效。
<javadoc>自定义的语法为:
<javadoc>
以<b>Javadoc<\b>格式的内容。
</javadoc>
或者
<javadoc>
<<![CDATA[以<b>Javadoc<\b>格式的内容]]>
</javadoc>
请注意,在包级别应用的<javadoc>声明中的文档字符串必须包含<body>开启和闭合标签;例如:
<jxb:package
name="primer.myPo">
<jxb:javadoc>
<![CDATA[<body>
生成的包primer.myPo的包级别文档。
</body>]]>
</jxb:javadoc>
</jxb:package>
所有标准的JAXB绑定声明必须以一个映射到JAXB命名空间URIhttp://java.sun.com/xml/ns/jaxb的命名空间前缀为前缀。例如,在此示例中使用jxb:。为此,您希望使用标准的JAXB绑定声明自定义的任何模式必须在模式文件的顶部包含JAXB命名空间声明和JAXB版本号。例如,在Customize Inline示例中的po.xsd中,命名空间声明如下:
<xsd:schema
xmlns:xsd= "http://www.w3.org/2001/XMLSchema"
xmlns:jxb= "http://java.sun.com/xml/ns/jaxb"
jxb:version="1.0">
具有jxb命名空间前缀的绑定声明采用以下形式:
<xsd:annotation>
<xsd:appinfo>
<jxb:globalBindings
绑定声明 />
<jxb:schemaBindings>
...
绑定声明 .
...
</jxb:schemaBindings>
</xsd:appinfo>
</xsd:annotation>
请注意,在此示例中,globalBindings和schemaBindings声明分别用于指定全局范围和模式范围的自定义。这些自定义范围在范围、继承和优先级中有更详细的描述。
定制内联示例展示了通过内联注释对名为po.xsd的XML模式进行的一些基本定制。此外,该示例实现了一个自定义数据类型转换器类MyDatatypeConverter.java,该类演示了<javaType>定制中的打印和解析方法,用于处理自定义数据类型转换。
总结这个示例:
要使用Ant编译和运行定制内联示例,请在终端窗口中进入jaxb-ri-install/samples/inline-customize/目录,然后输入以下内容:
ant
此示例中的关键定制和自定义的MyDatatypeConverter.java类将在下一节中详细描述。
定制内联示例中使用的定制模式在文件jaxb-ri-install/samples/inline-customize/po.xsd中。定制内容在<xsd:annotation>标记中。
以下代码示例显示了po.xsd中的globalBindings声明:
<jxb:globalBindings
fixedAttributeAsConstantProperty="true"
collectionType="java.util.Vector"
typesafeEnumBase="xsd:NCName"
choiceContentProperty="false"
typesafeEnumMemberName="generateError"
bindingStyle="elementBinding"
enableFailFastCheck="false"
generateIsSetMethod="false"
underscoreBinding="asCharInWord"/>
在这个示例中,除了collectionType之外,所有值都设置为默认值。
以下代码显示了po.xsd中的模式绑定声明:
<jxb:schemaBindings>
<jxb:package name="primer.myPo">
<jxb:javadoc>
<![CDATA[<body>
为生成的包primer.myPo提供包级文档。
</body>]]>
</jxb:javadoc>
</jxb:package>
<jxb:nameXmlTransform>
<jxb:elementName suffix="Element"/>
</jxb:nameXmlTransform>
</jxb:schemaBindings>
以下代码显示了po.xsd中的类绑定声明:
<xsd:complexType name="PurchaseOrderType">
<xsd:annotation>
<xsd:appinfo>
<jxb:class name="POType">
<jxb:javadoc>
一个采购订单由地址和项目组成。
</jxb:javadoc>
</jxb:class>
</xsd:appinfo>
</xsd:annotation>
<!-- ... -->
</xsd:complexType>
用于模式相关POType类的Javadoc工具注释将包含描述"一个采购订单由地址和项目组成。" 使用<转义开头的<b> HTML标签上的括号。
在po.xsd中的后面,另一个<javadoc>定制在这个类级别被声明,但这次HTML字符串是用CDATA进行转义:
<xsd:annotation>
<xsd:appinfo>
<jxb:class>
<jxb:javadoc>
<![CDATA[
USAddress的文档的第一行。]]>
</jxb:javadoc>
</jxb:class>
</xsd:appinfo>
</xsd:annotation>
特别重要的是generateIsSetMethod定制,它导致生成两个额外的属性方法isSetQuantity和unsetQuantity。这些方法使客户端应用程序能够区分模式默认值和在实例文档中显式出现的值。
例如,在po.xsd中:
<xsd:complexType name="Items">
<xsd:sequence>
<xsd:element name="item"
minOccurs="1"
maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
<xsd:element
name="productName"
type="xsd:string"/>
<xsd:element
name="quantity"
default="10">
<xsd:annotation>
<xsd:appinfo>
<jxb:property
generateIsSetMethod="true"/>
</xsd:appinfo>
</xsd:annotation>
<!-- ... -->
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
@generateIsSetMethod应用于quantity元素,它绑定到Items.ItemType接口中的一个属性。在Items.ItemType接口中生成了unsetQuantity和isSetQuantity方法。
类jaxb-ri-install/samples/inline-customize/src/inlinecustomize/primer/MyDatatypeConverter,如下例所示,提供了一种使用<javaType>定制来自定义XML数据类型与Java数据类型之间转换的方法。
package primer;
import java.math.BigInteger;
import javax.xml.bind.DatatypeConverter;
public class MyDatatypeConverter {
public static short parseIntegerToShort(String value) {
BigInteger result = DatatypeConverter.parseInteger(value);
return (short)(result.intValue());
}
public static String printShortToInteger(short value) {
BigInteger result = BigInteger.valueOf(value);
return DatatypeConverter.printInteger(result);
}
public static int parseIntegerToInt(String value) {
BigInteger result = DatatypeConverter.parseInteger(value);
return result.intValue();
}
public static String printIntToInteger(int value) {
BigInteger result = BigInteger.valueOf(value);
return DatatypeConverter.printInteger(result);
}
};
以下代码展示了如何在po.xsd中的<javaType>声明中引用MyDatatypeConverter类:
<xsd:simpleType name="ZipCodeType">
<xsd:annotation>
<xsd:appinfo>
<jxb:javaType name="int"
parseMethod="primer.MyDatatypeConverter.parseIntegerToInt"
printMethod="primer.MyDatatypeConverter.printIntTo Integer" />
</xsd:appinfo>
</xsd:annotation>
<xsd:restriction base="xsd:integer">
<xsd:minInclusive value="10000"/>
<xsd:maxInclusive value="99999"/>
</xsd:restriction>
</xsd:simpleType>
在这个例子中,jxb:javaType绑定声明覆盖了默认的JAXB绑定,将此类型绑定到java.math.BigInteger。对于自定义内联示例,ZipCodeType的限制(特别是,有效的美国邮政编码限制为五位数字)使得所有有效值都可以适应Java原始数据类型int。还要注意,因为<jxb:javaType name="int"/>在ZipCodeType中声明,所以这个定制适用于所有引用这个simpleType定义的JAXB属性,包括getZip和setZip方法。
数据类型转换器示例与自定义内联示例类似。与自定义内联示例一样,数据类型转换器示例通过在应用程序的XML模式po.xsd中使用内联绑定声明来进行定制。
定制内联和数据类型转换器示例的全局、模式、包和大部分类定制是相同的。数据类型转换器示例与自定义内联示例的区别在于用于将XML数据转换为Javaint数据类型的parseMethod和printMethod。
具体而言,数据类型转换器示例使用javax.xml.bind.DatatypeConverter提供的内置方法来执行这些数据类型转换,而不是使用自定义的MyDataTypeConverter类中的方法:
<xsd:simpleType name="ZipCodeType">
<xsd:annotation>
<xsd:appinfo>
<jxb:javaType
name="int"
parseMethod="javax.xml.bind.DatatypeConverter.parseInt"
printMethod="javax.xml.bind.DatatypeConverter.printInt"/>
</xsd:appinfo>
</xsd:annotation>
<xsd:restriction base="xsd:integer">
<xsd:minInclusive value="10000"/>
<xsd:maxInclusive value="99999"/>
</xsd:restriction>
</xsd:simpleType>
要使用Ant编译和运行DataType Converter示例,请在终端窗口中进入jaxb-ri-install/samples/datatypeconverter/目录,并输入以下命令:
ant
以下章节提供有关绑定声明文件的信息:
所有JAXB绑定声明文件必须以以下内容开始:
bindings.xjb中的版本、命名空间和模式声明如下:
<jxb:bindings
version="1.0"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<jxb:bindings
schemaLocation="po.xsd"
node="/xs:schema">
<!-- ...
binding-declarations
... -->
</jxb:bindings>
<!--
schemaLocation="po.xsd"
node="/xs:schema" -->
</jxb:bindings>
以<jaxb:bindings>为根元素的XML文件被视为外部绑定文件。根元素必须指定其绑定声明必须遵守的JAXB版本属性;具体而言,根<jxb:bindings>元素必须包含<jxb:version>声明或version属性。相比之下,当进行内联绑定声明时,JAXB版本号作为<xsd:schema>声明的属性:
<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
jxb:version="1.0">
如JAXB版本、命名空间和模式属性所示,外部绑定声明文件中的命名空间声明包括JAXB命名空间和XMLSchema命名空间。请注意,此示例中使用的前缀实际上可以是任何您想要的;重要的是要始终在文件中的后续声明中一致使用您在此定义的前缀。
代码中的第四行在JAXB版本、命名空间和模式属性中指定了绑定声明文件适用的模式名称和首次生效的模式节点。此文件中的后续绑定声明可以引用模式中的特定节点,但是第一个声明应包含整个模式;例如,在bindings.xjb中:
<jxb:bindings schemaLocation="po.xsd" node="/xs:schema">
bindings.xjb中的全局模式绑定声明与DataType Converter示例中的po.xsd中的声明相同。唯一的区别是,因为po.xsd中的声明是内联的,所以您必须将它们嵌入到<xs:appinfo>元素中,而这些元素又嵌入在<xs:annotation>元素中。在外部绑定文件中,嵌入声明是不必要的。
<jxb:globalBindings
fixedAttributeAsConstantProperty="true"
collectionType="java.util.Vector"
typesafeEnumBase="xs:NCName"
choiceContentProperty="false"
typesafeEnumMemberName="generateError"
bindingStyle="elementBinding"
enableFailFastCheck="false"
generateIsSetMethod="false"
underscoreBinding="asCharInWord"/>
<jxb:schemaBindings>
<jxb:package name="primer.myPo">
<jxb:javadoc>
<![CDATA[<body>
生成的包primer.myPo的包级文档
</body>]]>
</jxb:javadoc>
</jxb:package>
<jxb:nameXmlTransform>
<jxb:elementName suffix="Element"/>
</jxb:nameXmlTransform>
</jxb:schemaBindings>
相比之下,在DataType Converter示例的po.xsd中,用于全局模式绑定声明的语法为:
<xsd:annotation>
<xsd:appinfo>
<jxb:globalBindings
...
绑定声明
...
<jxb:schemaBindings>
...
绑定声明
...
</jxb:schemaBindings>
</xsd:appinfo>
</xsd:annotation>
bindings.xjb中的类级绑定声明与DataType Converter示例的po.xsd中的类似声明有两个不同之处:
<jxb:bindings node="//节点类型[@name=’节点名称’]">
例如,以下代码显示了名为USAddress的complexType的绑定声明。
<jxb:bindings node="//xs:complexType [@name=’USAddress’]">
<jxb:class>
<jxb:javadoc>
<![CDATA[
一个USAddress的文档的第一行。
]]>
</jxb:javadoc>
</jxb:class>
<jxb:bindings node=".//xs:element [@name=’name’]">
<jxb:property name="toName"/>
</jxb:bindings>
<jxb:bindings node=".//xs:element [@name=’zip’]">
<jxb:property name="zipCode"/>
</jxb:bindings>
</jxb:bindings>
<!--
node="//xs:complexType
[@name=’USAddress’]" -->
请注意,在此示例中,USAddress是子元素name和zip的父元素,因此</jxb:bindings>标签将子元素和类级别的javadoc声明的bindings声明包含在内。
外部自定义示例与数据类型转换示例相同,只是外部自定义示例中的绑定声明是使用外部绑定声明文件而不是内联在源XML模式中。
外部自定义示例中使用的绑定自定义文件是jaxb-ri-install/samples/external-customize/binding.xjb。
本节将bindings.xjb中的自定义声明与DataType Converter示例中的XML模式po.xsd中使用的类似声明进行比较。这两组声明完全实现相同的结果。
要使用Ant编译和运行外部自定义示例,请在终端窗口中转到jaxb-ri-install/samples/external-customize/目录,然后输入以下内容:
ant