Java 中文官方教程 2022 版(四十九)(3)https://developer.aliyun.com/article/1488535
JAXB 版本号
具有根元素 的 XML 文件被视为外部绑定文件。根元素必须指定其绑定声明必须遵守的 JAXB 版本属性;具体来说,根
元素必须包含
声明或
version
属性。相比之下,当进行内联绑定声明时,JAXB 版本号作为 声明的属性:
<xsd:schema jxb:version="1.0">
命名空间声明
如 JAXB 版本、命名空间和模式属性中所示,外部绑定声明文件中的命名空间声明包括 JAXB 命名空间和 XMLSchema 命名空间。请注意,此示例中使用的前缀实际上可以是任何您想要的;重要的是在文件中后续声明中一致使用您在此定义的任何前缀。
模式名称和模式节点
代码中的第四行在 JAXB 版本、命名空间和模式属性中指定了此绑定声明文件适用的模式的名称,以及首次生效的模式节点。此文件中的后续绑定声明可以引用模式中的特定节点,但此第一个声明应该涵盖整个模式;例如,在bindings.xjb
中:
<jxb:bindings schemaLocation="po.xsd" node="/xs:schema">
全局和模式绑定声明
bindings.xjb
中的全局模式绑定声明与数据类型转换器示例中的po.xsd
中的相同。唯一的区别是,因为po.xsd
中的声明是内联完成的,您必须将它们嵌入到元素中,而这些元素又嵌入到
元素中。以这种方式嵌入声明在外部绑定文件中是不必要的。
<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> Package level documentation for generated package primer.myPo.</body>]]> </jxb:javadoc> </jxb:package> <jxb:nameXmlTransform> <jxb:elementName suffix="Element"/> </jxb:nameXmlTransform> </jxb:schemaBindings>
相比之下,数据类型转换器示例中po.xsd
中使用的语法是:
<xsd:annotation> <xsd:appinfo> <jxb:globalBindings ... *binding-declarations* ... <jxb:schemaBindings> ... *binding-declarations* ... </jxb:schemaBindings> </xsd:appinfo> </xsd:annotation>
类声明
bindings.xjb
中的类级绑定声明与数据类型转换器示例中的po.xsd
中的类似声明有两个不同之处:
- 与
bindings.xjb
中的所有其他绑定声明一样,您不需要将自定义嵌入到模式元素中。
- 您必须指定应用自定义的模式节点。此类型声明的一般语法为:
<jxb:bindings node="//*node-type*[@name=’*node-name*’]">
例如,以下代码显示了名为USAddress
的complexType
的绑定声明。
<jxb:bindings node="//xs:complexType [@name=’USAddress’]"> <jxb:class> <jxb:javadoc> <![CDATA[ First line of documentation for a <b>USAddress</b>. ]]> </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
的父元素,因此标签将子元素和类级javadoc
声明的bindings
声明括起来。
外部自定义示例
外部自定义示例与数据类型转换器示例相同,只是外部自定义示例中的绑定声明是使用外部绑定声明文件而不是内联在源 XML 模式中完成的。
外部自定义示例中使用的绑定自定义文件是jaxb-ri-install/samples/external-customize/binding.xjb
。
本节将bindings.xjb
中的自定义声明与 XML 模式po.xsd
中的数据类型转换器示例中使用的类似声明进行比较。这两组声明实现完全相同的结果。
使用 Ant 构建和运行外部自定义示例
要使用 Ant 编译和运行外部自定义示例,在终端窗口中,转到jaxb-ri-install/samples/external-customize/
目录,并输入以下内容:
ant
Java-to-Schema 示例
Java-to-Schema 示例展示了如何使用注解将 Java 类映射到 XML 模式。
j2s-create-marshal 示例
j2s-create-marshal 示例演示了 Java-to-schema 数据绑定。它演示了对带有 JAXB 注解的类进行编组和解组,并展示了如何在解组时使用从 JAXB 映射类生成的模式文件启用 JAXP 1.3 验证。
schema 文件bc.xsd
是通过以下命令生成的:
schemagen src/cardfile/*.java cp schema1.xsd bc.xsd
请注意,schema1.xsd
被复制到bc.xsd
;schemagen
不允许您指定自己选择的模式名称。
使用 Ant 构建和运行 j2s-create-marshal 示例
要使用 Ant 编译和运行 j2s-create-marshal 示例,在终端窗口中,转到jaxb-ri-install/samples/j2s-create-marshal/目录并输入以下内容:
ant
j2s-xmlAccessorOrder 示例
j2s-xmlAccessorOrder 示例展示了如何使用@XmlAccessorOrder
和@XmlType.propOrder
注解来指定 Java 类型在编组和解组时的 XML 内容顺序。
使用 Java-to-schema 映射,JavaBean 的属性和字段被映射到 XML 模式类型。类元素被映射为 XML 模式复杂类型或 XML 模式简单类型。生成的模式类型的默认元素顺序目前未指定,因为 Java 反射不强制返回顺序。可靠的元素排序的缺失对应用程序的可移植性产生负面影响。您可以使用两个注解@XmlAccessorOrder
和@XmlType.propOrder
,为必须跨 JAXB 提供程序可移植的应用程序定义模式元素排序。
使用@XmlAccessorOrder
注解定义模式元素排序
@XmlAccessorOrder
注解强制执行两种元素排序算法,AccessorOrder.UNDEFINED
或AccessorOrder.ALPHABETICAL
。AccessorOrder.UNDEFINED
是默认设置。顺序取决于系统的反射实现。AccessorOrder.ALPHABETICAL
算法按java.lang.String.CompareTo(String anotherString)
确定的字典顺序对元素进行排序。
您可以为注解类型ElementType.PACKAGE
的类对象定义@XmlAccessorOrder
注解。当@XmlAccessorOrder
注解定义在包上时,格式规则的范围对包中的每个类都有效。当定义在类上时,规则对该类的内容有效。
一个包中可以有多个 @XmlAccessorOrder
注解。最内层(类)注解优先于外部注解。例如,如果在一个包中定义了 @XmlAccessorOrder(AccessorOrder.ALPHABETICAL)
,并且在该包中的一个类上定义了 @XmlAccessorOrder(AccessorOrder.UNDEFINED)
,则该类的生成的模式类型的内容将以未指定的顺序排列,而该包中的其他每个类的生成的模式类型的内容将按字母顺序排列。
使用 @XmlType
注解定义模式元素顺序
@XmlType
注解可以定义在一个类上。@XmlType
注解中的 propOrder()
元素使您能够指定生成的模式类型中的内容顺序。当您在一个类上使用 @XmlType.propOrder
注解来指定内容顺序时,类中的所有公共属性和公共字段必须在参数列表中指定。您希望保留在参数列表之外的任何公共属性或字段必须用 @XmlAttribute
或 @XmlTransient
注解进行标注。
@XmlType.propOrder
的默认内容顺序为 {}
或 {""}
,不活动。在这种情况下,活动的 @XmlAccessorOrder
注解优先。当类内容顺序由 @XmlType.propOrder
注解指定时,它优先于类或包上的任何活动的 @XmlAccessorOrder
注解。如果在一个类上指定了 @XmlAccessorOrder
和 @XmlType.propOrder(A, B, ...)
注解,那么 propOrder
总是优先,不管注解语句的顺序如何。例如,在以下代码段中,@XmlAccessorOrder
注解在 @XmlType.propOrder
注解之前。
@XmlAccessorOrder(AccessorOrder.ALPHABETICAL) @XmlType(propOrder={"name", "city"}) public class USAddress { // ... public String getCity() {return city;} public void setCity(String city) {this.city = city;} public String getName() {return name;} public void setName(String name) {this.name = name;} // ... }
在以下代码段中,@XmlType.propOrder
注解在 @XmlAccessorOrder
注解之前。
@XmlType(propOrder={"name", "city"}) @XmlAccessorOrder(AccessorOrder.ALPHABETICAL) public class USAddress { // ... public String getCity() {return city;} public void setCity(String city) {this.city = city;} public String getName() {return name;} public void setName(String name) {this.name = name;} // ... }
在两种情况下,propOrder
优先,并生成以下相同的模式内容:
<xs:complexType name="usAddress"> <xs:sequence> <xs:element name="name" type="xs:string" minOccurs="0"/> <xs:element name="city" type="xs:string" minOccurs="0"/> </xs:sequence> </xs:complexType>
示例中的模式内容排序
采购订单代码示例演示了使用包和类级别的 @XmlAccessorOrder
注解以及在类上使用 @XmlType.propOrder
注解来定义模式内容顺序的效果。
类 package-info.java
定义了包中 @XmlAccessorOrder
为 ALPHABETICAL
。类 PurchaseOrderType
中的公共字段 shipTo
和 billTo
受此规则影响,生成的模式内容顺序由此规则确定。类 USAddress
在类上定义了 @XmlType.propOrder
注解,演示了用户定义的属性顺序优先于生成的模式中的 ALPHABETICAL
顺序。
生成的模式文件可以在 jaxb-ri-install/samples/j2s-xmlAccessorOrder/build/schemas/
目录中找到。
使用 Ant 构建和运行 j2s-xmlAccessorOrder 示例
要使用 Ant 编译和运行 j2s-xmlAccessorOrder 示例,在终端窗口中,转到 jaxb-ri-install/samples/j2s-xmlAccessorOrder/
目录,并输入以下内容:
ant
j2s-xmlAdapter 示例
j2s-xmlAdapter 示例演示了如何使用XmlAdapter
接口和@XmlJavaTypeAdapter
注解为使用int
作为键和String
作为值的HashMap
(字段)提供自定义映射的 XML 内容的组合和解组。
接口XmlAdapter
和注解@XmlJavaTypeAdapter
用于在解组和组合期间对数据类型进行特殊处理。有各种 XML 数据类型,其表示方式不容易映射到 Java(例如,xs:DateTime
和xs:Duration
),以及 Java 类型不正确地映射到 XML 表示。例如,java.util.Collection
(如List
)和java.util.Map
(如HashMap
)的实现,或非 JavaBean 类。
为这类情况提供了XmlAdapter
接口和@XmlJavaTypeAdapter
注解。这种组合提供了一个可移植的机制,用于将 XML 内容读取和写入 Java 应用程序。
XmlAdapter
接口定义了数据读取和写入的方法。
/* * ValueType - Java class that provides an * XML representation of the data. * It is the object that is used for marshalling and * unmarshalling. * * BoundType - Java class that is used to * process XML content. */ public abstract class XmlAdapter<ValueType,BoundType> { // Do-nothing constructor for the derived classes. protected XmlAdapter() {} // Convert a value type to a bound type. public abstract BoundType unmarshal(ValueType v); // Convert a bound type to a value type. public abstract ValueType marshal(BoundType v); }
您可以使用@XmlJavaTypeAdapter
注解将特定的XmlAdapter
实现与Target
类型PACKAGE
、FIELD
、METHOD
、TYPE
或PARAMETER
关联。
j2s-xmlAdapter 示例展示了如何使用XmlAdapter
将 XML 内容映射到(自定义)HashMap
中并从中组合出来。类KitchenWorldBasket
中的HashMap
对象basket
使用int
类型的键和String
类型的值。这些数据类型应该反映在读取和写入的 XML 内容中,因此 XML 内容应如下示例所示:
<basket> <entry key="9027">glasstop stove in black</entry> <entry key="288">wooden spoon</entry> </basket>
为 Java 类型HashMap
生成的默认模式不反映所需的格式。
<xs:element name="basket"> <xs:complexType> <xs:sequence> <xs:element name="entry" minOccurs="0" maxOccurs="unbounded"> <xs:complexType> <xs:sequence> <xs:element name="key" minOccurs="0" type="xs:anyType"/> <xs:element name="value" minOccurs="0" type="xs:anyType"/> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element>
在默认的HashMap
模式中,键和值都是元素,并且是anyType
数据类型。XML 内容如下所示:
<basket> <entry> <key>9027</key> <value>glasstop stove in black</value> </entry> <entry> <key>288</key> <value>wooden spoon</value> </entry> </basket>
要解决这个问题,示例使用了两个 Java 类,PurchaseList
和PartEntry
,它们反映了用于解组和组合内容的所需模式格式。为这些类生成的 XML 模式如下:
<xs:complexType name="PurchaseListType"> <xs:sequence> <xs:element name="entry" type="partEntry" nillable="true" maxOccurs="unbounded" minOccurs="0"/> </xs:sequence> </xs:complexType> <xs:complexType name="partEntry"> <xs:simpleContent> <xs:extension base="xs:string"> <xs:attribute name="key" type="xs:int" use="required"/> </xs:extension> </xs:simpleContent> </xs:complexType>
类AdapterPurchaseListToHashMap
实现了XmlAdapter
接口。在类KitchenWorldBasket
中,使用@XmlJavaTypeAdapter
注解将AdapterPurchaseListToHashMap
与字段HashMap
basket
配对。这种配对导致在KitchenWorldBasket
上进行任何对应的组合或解组操作时,将调用AdapterPurchaseListToHashMap
的组合或解组方法。
使用 Ant 构建和运行 j2s-xmlAdapter 示例
要使用 Ant 编译和运行 j2s-xmlAdapter 示例,在终端窗口中,转到jaxb-ri-install/samples/j2s-xmlAdapter/
目录,然后输入以下内容:
ant
j2s-xmlAttribute 示例
j2s-xmlAttribute 示例展示了如何使用@XmlAttribute
注解来定义一个属性或字段被视为 XML 属性。
@XmlAttribute
注解将字段或 JavaBean 属性映射到 XML 属性。强加以下规则:
- 静态最终字段映射到 XML 固定属性。
- 当字段或属性是集合类型时,集合类型的项目必须映射到模式简单类型。
- 当字段或属性不是集合类型时,类型必须映射到模式简单类型。
遵循 JavaBean 编程范式时,属性由字段名上的get
和set
前缀定义。
int zip; public int getZip(){return zip;} public void setZip(int z){zip=z;}
在 bean 类中,您可以选择在三个组件之一上设置@XmlAttribute
注解:字段、setter 方法或 getter 方法。如果在字段上设置@XmlAttribute
注解,则必须重命名 setter 方法,否则将在编译时出现命名冲突。如果在其中一个方法上设置@XmlAttribute
注解,则必须在 setter 或 getter 方法上设置,但不能同时设置在两者上。
XmlAttribute 示例展示了如何在静态最终字段上使用@XmlAttribute
注解,在字段而不是相应的 bean 方法上使用,在 bean 属性(方法)上使用,以及在不是集合类型的字段上使用。在类USAddress
中,字段 country 和 zip 被标记为属性。setZip
方法被禁用以避免编译错误。属性 state 在 setter 方法上被标记为属性。您也可以使用 getter 方法。在类PurchaseOrderType
中,字段cCardVendor
是非集合类型。它符合简单类型的要求;它是一个enum
类型。
使用 Ant 构建和运行 j2s-xmlAttribute 示例
要使用 Ant 编译和运行 j2s-xmlAttribute 示例,在终端窗口中,转到jaxb-ri-install/samples/j2s-xmlAttribute/
目录并键入以下内容:
ant
j2s-xmlRootElement 示例
j2s-xmlRootElement 示例演示了如何使用@XmlRootElement
注解为相应类的 XML 模式类型定义 XML 元素名称。
@XmlRootElement
注解将类或enum
类型映射到 XML 元素。每个用于解组和组装的顶级 Java 类型都需要至少一个元素定义。如果没有元素定义,XML 内容处理就没有起始位置。
@XmlRootElement
注解使用类名作为默认元素名。您可以通过使用注解属性name
来更改默认名称。如果这样做,指定的名称将用作元素名和类型名。元素和类型名称不同是常见的模式实践。您可以使用@XmlType
注解来设置元素类型名称。
@XmlRootElement
注解的命名空间属性用于为元素定义命名空间。
使用 Ant 构建和运行 j2s-xmlRootElement 示例
要使用 Ant 编译和运行 j2s-xmlRootElement 示例,在终端窗口中,转到jaxb-ri-install/samples/j2s-xmlRootElement/
目录并输入以下内容:
ant
j2s-xmlSchemaType 示例
j2s-xmlSchemaType 示例演示了如何使用注解@XmlSchemaType
自定义将属性或字段映射到 XML 内置类型的映射。
@XmlSchemaType
注解可用于将 Java 类型映射到 XML 内置类型之一。此注解在将 Java 类型映射到九种日期/时间原始数据类型之一时最有用。
当在包级别定义@XmlSchemaType
注解时,标识需要同时包含 XML 内置类型名称和相应的 Java 类型类。在字段或属性上的@XmlSchemaType
定义优先于包定义。
XmlSchemaType 类示例展示了如何在包级别、字段和属性上使用@XmlSchemaType
注解。文件TrackingOrder
有两个字段,orderDate
和deliveryDate
,它们被定义为XMLGregorianCalendar
类型。生成的模式将定义这些元素为 XML 内置类型gMonthDay
。此关系在文件package-info.java
中在包中定义。文件TrackingOrder
中的shipDate
字段也被定义为XMLGregorianCalendar
类型,但@XmlSchemaType
注解语句覆盖了包定义,并指定该字段为date
类型。属性方法getTrackingDuration
定义模式元素被定义为原始类型duration
而不是 Java 类型String
。
使用 Ant 构建和运行 j2s-xmlSchemaType 示例
要使用 Ant 编译和运行 j2s-xmlSchemaType 示例,在终端窗口中,转到jaxb-ri-install/samples/j2s-xmlSchemaType/
目录并输入以下内容:
ant
j2s-xmlType 示例
j2s-xmlType 示例演示了如何使用@XmlType
注解。@XmlType
注解将一个类或一个enum
类型映射到一个 XML Schema 类型。
一个类必须具有公共零参数构造函数或静态零参数工厂方法,以便通过此注解进行映射。在解组期间,其中一个方法用于创建类的实例。工厂方法可以位于工厂类中或现有类中。
有一个关于解组使用哪种方法的优先顺序:
- 如果注解中标识了工厂类,则该类中还必须标识相应的工厂方法,并且该方法将被使用。
- 如果注解中标识了工厂方法但未标识工厂类,则工厂方法必须位于当前类中。即使存在公共零参数构造方法,也将使用工厂方法。
- 如果注解中未标识工厂方法,则类必须包含一个公共零参数构造方法。
在这个例子中,一个工厂类为几个类提供了零参数的工厂方法。类OrderContext
上的@XmlType
注解引用了工厂类。解组器在这个类中使用了标识的工厂方法。
public class OrderFormsFactory { public OrderContext newOrderInstance() { return new OrderContext() } public PurchaseOrderType { newPurchaseOrderType() { return new newPurchaseOrderType(); } } @XmlType(name="oContext", factoryClass="OrderFormsFactory", factoryMethod="newOrderInstance") public class OrderContext { public OrderContext() { // ... } } }
在这个例子中,一个工厂方法在一个类中被定义,该类还包含一个标准的类构造。因为factoryMethod
的值被定义了,而没有定义factoryClass
,所以在解组时会使用工厂方法newOrderInstance
。
@XmlType(name="oContext", factoryMethod="newOrderInstance") public class OrderContext { public OrderContext() { // ... } public OrderContext newOrderInstance() { return new OrderContext(); } }
使用 Ant 构建和运行 j2s-xmlType 示例
要使用 Ant 编译和运行 j2s-xmlType 示例,在终端窗口中,进入jaxb-ri-install/samples/j2s-xmlType/目录,并输入以下内容:
ant