38.2. 指定 XML 架构 Primitive 的 Java 类

概述

默认情况下,XML Schema 类型映射到 Java 原语类型。虽然这是 XML 架构与 Java 之间的最逻辑映射,但它并不总是满足应用程序开发人员的要求。您可能希望将 XML 架构原语类型映射到可容纳额外信息的 Java 类,或者您可能希望将 XML 原语类型映射到允许简单类型替换的类。

JAXB javaType 自定义元素允许您自定义 XML 架构原语类型和 Java 原语类型之间的映射。它可用于在全局级别和个别实例级别自定义映射。您可以将 javaType 元素用作简单类型定义的一部分,也可以用作复杂类型定义的一部分。

当使用 javaType 自定义元素时,您必须指定将 primitive 类型的 XML representation 转换为目标 Java 类的方法。有些映射有默认的转换方法。对于没有默认映射的实例,Apache CXF 提供 JAXB 方法来简化所需方法的开发。

语法

javaType 自定义元素采用四个属性,如 表 38.1 “用于自定义 Java 类生成 XML 架构类型的属性” 所述。

表 38.1. 用于自定义 Java 类生成 XML 架构类型的属性

属性必填描述

name

指定将 XML 架构原语类型映射到的 Java 类的名称。它必须是有效的 Java 类名称或 Java 原语类型的名称。您必须确保这个类存在,且可以被您的应用程序访问。这个类的代码生成器没有检查。

xmlType

指定正在自定义的 XML 架构原语类型。此属性仅在 javaType 元素用作 globalBindings 元素的子级时使用。

parseMethod

指定负责将数据的基于字符串 XML representation 的方法解析为 Java 类的实例。更多信息请参阅 “指定转换器”一节

printMethod

指定负责将 Java 对象转换为数据的字符串 XML 表示的方法。更多信息请参阅 “指定转换器”一节

javaType 自定义元素可通过三种方式使用:

  • 要修改 XML 架构模拟类型的所有实例 - javaType 元素在 schema 文档中修改 XML 架构类型的所有实例(当作为 全局Bindings 自定义元素的子项)。以这种方式使用时,您必须为 xmlType 属性指定一个值,用于标识正在修改的 XML 架构原语类型。

    例 38.7 “全球原语类型自定义” 显示在线全局自定义,指示代码生成器在架构中将 java.lang.Integer 用于 xsd:short 的所有实例。

    例 38.7. 全球原语类型自定义

    <schema targetNamespace="http://widget.com/types/widgetTypes"
            xmlns="http://www.w3.org/2001/XMLSchema"
            xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
            xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
            jaxb:version="2.0">
      <annotation>
        <appinfo>
          <jaxb:globalBindings ...>
            <jaxb:javaType name="java.lang.Integer"
                           xmlType="xsd:short" />
          </globalBindings
        </appinfo>
      </annotation>
      ...
    </schema>
  • 要修改简单类型定义 - javaType 元素会在应用到命名的简单类型定义时修改为 XML 简单类型所有实例生成的类。当使用 javaType 元素修改简单类型定义时,不要使用 xmlType 属性。

    例 38.8 “用于自定义简单类型的绑定文件” 显示修改名为 zipCode 的简单类型的外部绑定文件。

    例 38.8. 用于自定义简单类型的绑定文件

    <jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
                   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                   jaxb:version="2.0">
      <jaxb:bindings wsdlLocation="widgets.wsdl">
        <jaxb:bindings node="xsd:simpleType[@name='zipCode']">
            <jaxb:javaType name="com.widgetVendor.widgetTypes.zipCodeType"
                           parseMethod="com.widgetVendor.widgetTypes.support.parseZipCode"
                           printMethod="com.widgetVendor.widgetTypes.support.printZipCode" />
        </jaxb:bindings>
      </jaxb:bindings>
    <jaxb:bindings>
  • 要修改复杂类型定义的元素或属性 - javaType 可以应用到复杂类型定义的个别部分,方法是将其包含在 JAXB 属性自定义中。javaType 元素作为子项放在属性的 baseType 元素中。当使用 javaType 元素修改复杂类型定义的特定部分时,不要使用 xmlType 属性。

    例 38.9 “在复杂类型自定义 Element 的绑定文件” 显示修改复杂类型的元素的绑定文件。

    例 38.9. 在复杂类型自定义 Element 的绑定文件

    <jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
                   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                   jaxb:version="2.0">
      <jaxb:bindings schemaLocation="enumMap.xsd">
        <jaxb:bindings node="xsd:ComplexType[@name='widgetOrderInfo']">
          <jaxb:bindings node="xsd:element[@name='cost']">
            <jaxb:property>
              <jaxb:baseType>
                <jaxb:javaType name="com.widgetVendor.widgetTypes.costType"
                                parseMethod="parseCost"
                                printMethod="printCost" >
              </jaxb:baseType>
            </jaxb:property>
          </jaxb:bindings>
        </jaxb:bindings>
      </jaxb:bindings>
    <jaxb:bindings>

    有关使用 baseType 元素的详情请参考 第 38.6 节 “指定 Element 或属性的基本类型”

指定转换器

Apache CXF 无法将 XML 架构原始类型转换为随机 Java 类。当您使用 javaType 元素自定义 XML 架构原语类型映射时,代码生成器会创建一个用于打包和解包自定义 XML 架构原语类型的适配器类。例 38.10 “JAXB Adapter 类” 显示了一个示例适配器类。

例 38.10. JAXB Adapter 类

public class Adapter1 extends XmlAdapter<String, javaType>
{
  public javaType unmarshal(String value)
  {
    return(parseMethod(value));
  }

  public String marshal(javaType value)
  {
    return(printMethod(value));
  }
}

parseMethodprintMethod 替换为对应的 parseMethod 属性的值和 printMethod 属性。该值必须识别有效的 Java 方法。您可以通过两种方式之一指定方法的名称:

  • 完全限定的 Java 方法名称,格式为 packagename.ClassName.methodName
  • methodName形式的简单方法名称

    当您只提供简单方法名称时,代码生成器会假定 javaType 元素的 name 属性指定的类中存在方法。

重要

代码生成器 不会生成 解析或打印方法。您负责提供它们。有关开发解析和打印方法的详情,请参考 “实现转换器”一节

如果没有提供 parseMethod 属性的值,则 code 生成器会假定 name 属性指定的 Java 类具有构造器,其第一个参数是 Java String 对象。生成的适配器的 unmarshal() 方法使用假定的构造器使用 XML 数据填充 Java 对象。

如果没有提供 printMethod 属性的值,则由 name 属性指定的 Java 类具有 toString() 方法。生成的适配器的 marshal() 方法使用 assumed toString() 方法将 Java 对象转换为 XML 数据。

如果 javaType 元素的 name 属性指定 Java 原语类型,或者 Java 原语类型之一,则代码生成器将使用默认转换器。有关默认转换器的更多信息,请参阅 “默认原语类型转换器”一节

生成的内容

“指定转换器”一节 所述,使用 javaType 自定义元素触发为每个 XML 架构小语自定义一个适配器类的生成。适配器使用模式 适配器N 按顺序命名。如果您指定了两个原语类型自定义,则代码生成器会创建两个适配器类: Adapter1Adapter2

为 XML 模式构建生成的代码取决于生效的 XML 架构结构是全局定义的元素,或者定义为复杂类型的一部分。

当 XML 架构结构是全局定义的元素时,从默认方法中修改为类型生成的对象工厂方法,如下所示:

  • 该方法与 @XmlJavaTypeAdapter 注释进行解码。

    该注解指示处理此元素实例时要使用的适配器类的运行时。适配器类指定为类对象。

  • 默认类型由 javaType 元素的 name 属性指定的类替代。

例 38.11 “用于全局元素的自定义对象因素方法” 显示受 例 38.7 “全球原语类型自定义” 中显示的自定义影响元素的对象工厂方法。

例 38.11. 用于全局元素的自定义对象因素方法

 @XmlElementDecl(namespace = "http://widgetVendor.com/types/widgetTypes", name = "shorty")
 @XmlJavaTypeAdapter(org.w3._2001.xmlschema.Adapter1.class)
 public JAXBElement<Integer> createShorty(Integer value) {
     return new JAXBElement<Integer>(_Shorty_QNAME, Integer.class, null, value);
 }

当将 XML 架构结构定义为复杂类型的一部分时,生成的 Java 属性将进行修改,如下所示:

  • 属性被解码为 @XmlJavaTypeAdapter 注释。

    该注解指示处理此元素实例时要使用的适配器类的运行时。适配器类指定为类对象。

  • 该属性的 @XmlElement 包含一个 类型 属性。

    type 属性的值是代表生成对象的默认基础类型的类对象。如果是 XML 架构原语类型,该类为 String

  • 属性通过 @XmlSchemaType 注释进行解码。

    该注释标识结构的 XML 架构原语类型。

  • 默认类型由 javaType 元素的 name 属性指定的类替代。

例 38.12 “自定义复杂类型” 显示受 例 38.7 “全球原语类型自定义” 中显示的自定义影响元素的对象工厂方法。

例 38.12. 自定义复杂类型

public class NumInventory {

    @XmlElement(required = true, type = String.class) @XmlJavaTypeAdapter(Adapter1.class) @XmlSchemaType(name = "short") protected Integer numLeft;
    @XmlElement(required = true)
    protected String size;

    public Integer getNumLeft() {
        return numLeft;
    }

    public void setNumLeft(Integer value) {
        this.numLeft = value;
    }

    public String getSize() {
        return size;
    }

    public void setSize(String value) {
        this.size = value;
    }

}

实现转换器

Apache CXF 运行时不知道如何将 XML 原语类型转换为 javaType 元素指定的 Java 类,但它应调用由 parseMethod 属性和 printMethod 属性指定的方法。您需要提供运行时调用的方法实施。实施的方法必须能够使用 XML 原语类型的 lexical 结构。

为简化数据转换方法的实现,Apache CXF 提供 javax.xml.bind.DatatypeConverter 类。此类提供解析和打印所有 XML 架构原语类型的方法。解析方法使用 XML 数据的字符串表示,它们返回 表 34.1 “XML Schema 原语类型到 Java 原生类型映射” 中定义的默认类型实例。打印方法采用默认类型的实例,它们返回 XML 数据的字符串表示。

DatatypeConverter 类的 Java 文档可在 https://docs.oracle.com/javase/8/docs/api/javax/xml/bind/DatatypeConverter.html 中找到。

默认原语类型转换器

当指定 Java 原语类型或 Java 原语类型 Wrapper 类时,在 javaType 元素的 name 属性中不需要指定 parse Method 属性的值。如果没有提供值,Apache CXF 运行时会替换默认转换器。

默认数据转换器使用 JAXB DatatypeConverter 类来解析 XML 数据。默认转换器也将提供进行转换所需的任何类型转换器。