内容


使用 IBM Rational Software Architect 在 Java 中处理 XSD

创建使用了 Java 中不可用的 XSD 特性并用 XSD 描述的数据结构的 Java 表示形式

Comments

引言

许多应用程序需要以一种或多种使用 XSD 指定的行业标准消息格式表示数据。XSD 通常是指定行业标准消息格式的理想方法,因为它跨平台和编程语言受到广泛支持(无论是本机支持还是通过库支持)。但是,即使拥有此支持,由于特定于应用程序的数据结构与 XSD 之间的特性不匹配,将存储在这些特定于应用程序的数据结构中的数据转换为符合 XSD 的消息(通常为 XML 消息)会带来挑战。

图 1. 在两种特定于应用程序的数据结构之间提供公共消息模型的行业标准 XSD(中心)
提供公共消息模型的行业标准 XSD
提供公共消息模型的行业标准 XSD

在实现基于 Java 的 Web 服务时,这些特性不匹配会导致很难使用基于 Java API for XML-based RPC (JAX-RPC)、Java API for XML Web Services (JAX-WS) 或同时基于这两者的工具包,自动化从特定于应用程序的数据结构到 XML 消息的映射。

由于 XSD 与编程语言无关,它具有许多在 Java 对象中不直接受到支持的特性。此类特性的示例包括 xsd:choicexsd:group 数据类型以及 xsd:restriction 属性。本文向您介绍如何创建消除这些数据类型的 Java 友好的 XSD。您将使用基于 XSL Transformation (XSLT) 的映射,从而将基于 Java 友好的(内部)XSD 的消息转换为全功能的(外部)XSD。对于本文中的特定示例,外部 XSD 为 Postsecondary Electronic Standards Council (PESC) 的 Common Record: CommonLine (CRC) 助学贷款消息标准。

本文中用于创建与 CRC 1.4 XSD 相对应的 Java 友好的 XSD 的技术可应用于任何太复杂而在基于 Java 的 Web 服务工具包中不直接受支持的 XSD。首先让我们看一下大致步骤:

  1. 使用 JAX-RPC 或 JAX-WS 为 XSD 接口生成服务实现(和测试客户端)。
  2. 如果需要,则创建外部 XSD 的 Java 友好的简化版本。
  3. 使用 Java 友好的 XSD 重新生成服务实现。
  4. 实现 JAX-RPC 或 JAX-WS 代码生成当前不支持的 XSD 约束。

系统要求

要按照本文所述进行操作,需要在系统上安装以下软件:

  • 带 IBM WebSphere® Application Server Feature Pack for Web Services 的 IBM® Rational® Software Architect V7.0.0.6
  • IBM WebSphere Integration Developer V6.1

Feature Pack for Web Services 是 Rational Software Architect V7 的一个可选包,您可以使用 IBM Installation Manager 对其进行下载和安装。安装该功能包之后,确保通过在 Rational Software Architect 中单击 Window > Preferences > General > Capabilities 启用 Web Services Developer 功能。

导入外部 XSD

您的第一个步骤是导入外部 XSD 并基于该 XSD 创建 Web 服务描述语言(Web Services Description Language,WSDL)接口:

  1. 在 Rational Software Architect V7 中创建一个 Java 项目(例如,CRCLib)。
  2. PESC 网站导入该 XSD。
    图 2. 构成 CRC 标准的 XSD
    构成 CRC 标准的 XSD
  3. 通过右键单击 CRCLib 项目并选择 New > Other > WSDL 创建新的 WSDL 接口。
  4. 在 com.ibm.jxsd.services 目录中创建名为 LoanProcessing.wsdl 的接口。
  5. 将操作名称更改为 process,将输入更改为 CommonRecordCommonlineReceipt,将输出更改为 CommonRecordCommonlineResponse
    图 3. 在 Rational Software Architect 中创建 WSDL 接口
    在 Rational Software Architect 中创建 WSDL 接口
    在 Rational Software Architect 中创建 WSDL 接口

创建 JAX-RPC 实现

下一步,您将创建服务实现。实现技术的选择将决定您对内部 XSD 所做的更改。给定基于 WebSphere Application Server 的部署环境,总共存在两种技术选择:JAX-RPC 和 JAX-WS。JAX-WS 是一种较新的规范,比 JAX-RPC 支持更多的 XSD 构造,因此不需要单独的(内部)XSD。JAX-RPC 是一种较旧的规范,并且需要内部 XSD;但是由于它较旧,因此受到比 JAX-WS 更广泛的支持和采用。

本文介绍 JAX-RPC 和 JAX-WS 实现所需要的更改,首先从 JAX-RPC 开始:

  1. 右键单击 LoanProcessing.wsdl 文件并选择 Web Service > Generate Java bean Skeleton
  2. 使用 Generate Java bean Skeleton 向导,选择一个新的项目名称并确保 Web 服务运行时为 JAX-RPC
  3. 单击 Finish 生成代码。您应该看到一个窗口打开并显示警告。让我们检查一下其中一些警告,并了解为什么会发生这些警告。

xsd:group

无法在 Java 中通过 JAX-RPC 映射的 XSD 特性之一是 xsd:group 数据类型。它在 XSD 中的存在导致类似于清单 1 所示的 JAX-RPC 警告。

清单 1. 由于 xsd:group 而产生的 JAX-RPC 警告
WSWS3029W: Warning: The xml construct named 
{urn:org:pesc:core:CoreMain:v1.4.0}OrganizationType cannot be 
mapped to a java type.  The construct will be mapped to 
javax.xml.soap.SOAPElement.

由于 xsd:group 数据类型无法在 JAX-RPC 中进行明确的映射,因此一般将其映射到 Java 中的 SOAPElementSOAPElement 需要以编程方式映射到 xsd:group 才能正常工作(请参见清单 2 和 3)。

清单 2. 包含 xsd:group 元素的 XSD
<xs:group name="OrganizationIDGroup">
...
</xs:group>

<xs:complexType name="OrganizationType">
 <xs:sequence>
  <xs:group ref="core:OrganizationIDGroup"/>
   ...
 </xs:sequence>
</xs:complexType>
清单 3. 为 xsd:group 元素生成的 JAX-RPC 代码
...
 private javax.xml.soap.SOAPElement[] organizationType; 
...

替代方法

以编程方式将生成的 SOAPElement 映射到 xsd:group 数据类型是一种脆弱的解决方案,并违反了尽可能避免直接修改所生成的代码的实践。您不能更改来自于标准的 XSD,因此可以创建一个内部 XSD,在其中将 xsd:group 替换为 xsd:complexType,如清单 4 所示。

清单 4. 在其中将 xsd:group 元素替换为 xsd:complexType 的 XSD
<xs:complexType name="OrganizationIDGroup">
...
</xs:complexType>

<xs:complexType name="OrganizationType">
 <xs:sequence>
  <xs:element ref="core:OrganizationIDGroup"/>
   ...
 </xs:sequence>
</xs:complexType>

虽然 xsd:groupxsd:complexType 都是容器数据类型,但它们并不完全相同。组封装 allchoicesequence 元素,而 complexType 封装属性、嵌套元素和混合内容。因此您可能担心在转换到 xsd:complexType 时会失去 xsd:group 数据类型的某些表达功能。但是结果证明,xsd:choice 也不受 JAX-RPC 映射的支持,因此您无论如何都要在内部 XSD 中对此进行更改。xsd:sequencesxsd:complexType 中受支持(如清单 4 所示)。

xsd:choice

xsd:choice 是另一个不受 JAX-RPC 支持的 XSD 特性。针对 xsd:choice 的警告与清单 5 所示类似。

清单 5. 由于 xsd:choice 而产生的 JAX-RPC 警告
WSWS3029W: Warning: The xml construct named 
{urn:org:pesc:core:CoreMain:v1.4.0}TestsType cannot be mapped to a 
java type.  The construct will be mapped to 
javax.xml.soap.SOAPElement.

xsd:choice 数据类型描述了这样一个结构,其中只有一个子元素可以是活动的或者可供选择。Java 中不存在对应的结构,因此 JAX-RPC 同样将 xsd:choice 数据类型映射到通用 SOAPElement(请参见清单 6 和 7)。

清单 6. 包含 xsd:choice 元素的 XSD
<xs:complexType name="TestsType">
 <xs:sequence>
  ...
   <xs:choice>
    <xs:element name="TestDate" type="core:TestDateType"/>
    <xs:element name="TestYearMonth" type="core:TestYearMonthType"/>
    <xs:element name="TestYear" type="core:TestYearType"/>
   </xs:choice>
  ...
 </xs:sequence>
</xs:complexType>
清单 7. 为 xsd:choice 元素生成的 JAX-RPC 代码
...
 private javax.xml.soap.SOAPElement[] testType; 
...

替代方法

要使您的 XSD 变得 Java 友好,您可以从出现 xsd:choice 的位置将其完全删除,以便 JAX-RPC 能够产生映射。没有 xsd:choice 时,JAX-RPC 生成的代码将创建正确的类(请参见清单 8)。

清单 8. 在其中将 xsd:group 元素替换为 xsd:complexType 的 XSD
<xs:complexType name="OrganizationIDGroup">
...
</xs:complexType>

<xs:complexType name="OrganizationType">
 <xs:sequence>
  <xs:element ref="core:OrganizationIDGroup"/>
   ...
 </xs:sequence>
</xs:complexType>

这可能让人担心 XSD 将允许消息有多个元素,从而与外部 XSD 冲突。这种数据结构约束的弱化要求根据需要强化应用程序逻辑中的约束。但是,由于传入数据来自于外部来源,并满足外部 XSD,而传出数据在传输前将根据外部 XSD 进行验证,因此不存在应用程序接受或传输无效数据的危险。如果需要根据 XSD 约束对传入数据或传出数据进行权威验证,可以考虑部署专用的网络边缘设备,例如 IBM WebSphere DataPower SOA Appliances 所提供的设备。

Russell Butek 的文章“Web 服务技巧: 将多态性作为 xsd:choice 的备选方法”描述了对 xsd:choice 的另一种替代选择。

将内部 XSD 映射到外部 XSD

在完成从外部 XSD 到内部 XSD 的所有更改之后,您可以在 WebSphere Integration Developer V6.1 中创建一个中介模块,此模块将在 IBM WebSphere Enterprise Service Bus 或在 IBM WebSphere Process Server 中运行。在该中介模块中,一个 XSLT 映射在内部和外部 XSD 之间转换 XML 消息(请参见图 4)。

创建中介流和 XSLT 映射超出了本文的范围,但是WebSphere Integration Developer 帮助文档的构建中介流部分对此进行了详细的描述。 此外,为了便于参考,下载部分作为项目交换文件提供了为本文创建的中介模块。

请参见图 4 的大图

图 4. 在 WebSphere Integration Developer 中将内部 XSD 映射到外部 XSD
在 WebSphere Integration Developer 中将内部 XSD 映射到外部 XSD
在 WebSphere Integration Developer 中将内部 XSD 映射到外部 XSD

创建 JAX-WS 实现

在实际实现中,您将选择一种实现技术:JAX-RPC 或 JAX-WS。但是出于完整性的考虑,下面介绍了在使用 JAX-WS 时需要对内部 XSD 做出的更改:

  1. 在 CRCLib 项目中,右键单击 LoanProcessing.wsdl 文件,然后选择 Web Services > Generate Java bean skeleton
    图 5. 在 WSDL 基础上生成 Web 服务
    在 WSDL 基础上生成 Web 服务
    在 WSDL 基础上生成 Web 服务
  2. 在窗口中,将 Web 服务运行时更改为 IBM WebSphere JAX-WS。如果 JAX-WS 选项未列出,这意味着未安装 WebSphere Application Server Feature Pack for Web Services。
    图 6. 用于生成 JAX-WS Web 服务的选项
    用于生成 JAX-WS Web 服务的选项
    用于生成 JAX-WS Web 服务的选项
  3. 确保在窗口中选择新的项目名称(例如 LoanPWS),然后单击 Next 以确保将 WSDL 复制到项目。
  4. 回到 Service Deployment Configuration 窗口,单击 OK 生成所有代码。如果服务器未运行,它将会启动。

生成的 Java

代码成功生成而无任何错误或警告。成功地为整个 XSD 生成了对应的类,因此不需要任何单独的内部 XSD。JAX-WS 实现将 xsd:group 数据类型(此数据类型没有对应的 Java 表示形式)转换为 xsd:complexType。这是您在为 JAX-RPC 实现的内部 XSD 中手动执行的相同转换;但是在 JAX-WS 实现中,该转换是隐式和自动的。

xsd:choice

JAX-WS 实现将 xsd:choice 数据类型表示为 Java 类的属性,并且做出隐式和自动的转换,同样,此转换与您使用内部 XSD 为 JAX-RPC 实现执行的转换相同。

确保仅设置其中一个选择。例如,在清单 9 中,eSignatureCustodianType 中可以具有 LenderGuarantor 元素。

清单 9. 包含 xsd:choice 元素的 XSD
<xs:complexType name="eSignatureCustodianType">
 <xs:choice>
  <xs:element name="Lender">
   <xs:complexType>
    <xs:sequence>
     <xs:group ref="core:OrganizationIDGroup"/>
     <xs:element name="NonEDBranchID"
      type="core:NonEDBranchIDType" minOccurs="0"/>
    </xs:sequence>
   </xs:complexType>
  </xs:element>
  <xs:element name="Guarantor">
   <xs:complexType>
    <xs:sequence>
     <xs:group ref="core:OrganizationIDGroup"/>
     <xs:element name="NonEDBranchID" 
      type="core:NonEDBranchIDType" minOccurs="0"/>
    </xs:sequence>
   </xs:complexType>
  </xs:element>
 </xs:choice>
</xs:complexType>

为此类型生成的 Java 代码如清单 10 所示。

清单 10. 为 xsd:choice 元素生成的 JAX-WS 代码
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "eSignatureCustodianType", 
namespace = "urn:org:pesc:core:CoreMain:v1.4.0", 
propOrder = {
 "lender",
 "guarantor"
})
public class ESignatureCustodianType {

 @XmlElement(name = "Lender")
 protected ESignatureCustodianType.Lender lender;
 @XmlElement(name = "Guarantor")
 protected ESignatureCustodianType.Guarantor guarantor;

请注意,LenderGuarantor 都存在 get 和 set 方法。您必须确保仅填充其中一个。

不兼容性

XSD 数据类型可以描述在 Java 没有等效表达式时的约束。因此,JAX-RPC 和 JAX-WS 都不能创建保留所有约束的对应 Java 类。让我们看看 XSD 约束和 JAX-WS 中生成的对应 Java 代码的一些示例。

长度约束

在清单 11 中的 XSD 中,confirmationID 是一个最小长度为 1 和最大长度为 20 的字符串。

清单 11. 带长度约束的 XSD
<xs:simpleType name="ConfirmationIDType">
 <xs:annotation>
  <xs:documentation> Confirmation ID supplied 
  from the counseling session</xs:documentation>
 </xs:annotation>
 <xs:restriction base="xs:string">
  <xs:minLength value="1"/>
  <xs:maxLength value="20"/>
 </xs:restriction>
</xs:simpleType>

生成的 Java 代码如清单 12 所示。

清单 12. 为带长度约束的 XSD 生成的 Java 代码
public class LoanCounselingType {

 @XmlElement(name = "ConfirmationID")
 protected String confirmationID;
 @XmlElement(name = "CompletionDate")
 protected XMLGregorianCalendar completionDate;
 @XmlElement(name = "CompletionTime")
 protected XMLGregorianCalendar completionTime;

confirmationID 生成的 Java 代码只是一个不带任何约束的 Java 字符串。如果将该字符串设置为无效长度的值,该代码没有问题,但是生成的 XML 将无法通过验证。为了避免这一点,您必须显式地向代码添加字符串长度检查。

必备约束

另一个在 Java 中不受支持的 XSD 约束是必备 约束。例如,下面的 XSD 描述了一组必须从中选择其一的枚举值(也就是说,值为 Null 或不做出选择将是无效的)。

清单 13. 带必备约束的 XSD
<xs:simpleType name="PromissoryNoteDeliveryCodeType">
 <xs:annotation>
  <xs:documentation>Code indicating the preferred or 
  actual method of delivering the promissory note to 
  the borrower</xs:documentation>
 </xs:annotation>
 <xs:restriction base="xs:string">
  <xs:minLength value="1"/>
  <xs:enumeration value="Paper"/>
  <xs:enumeration value="Email"/>
  <xs:enumeration value="Web"/>
 </xs:restriction>
</xs:simpleType>

生成的 JAX-WS 代码如清单 14 所示。

清单 14. 为带必备约束的 XSD 生成的 Java 代码
public enum PromissoryNoteDeliveryCodeType {
 @XmlEnumValue("Paper")
 PAPER("Paper"),
 @XmlEnumValue("Email")
 EMAIL("Email"),
 @XmlEnumValue("Web")
 WEB("Web");
 private final String value;

代码生成器正确地创建了 Java 枚举,但是不确保至少选择其中一个值。因此,强制执行此约束同样是您的责任。

maxOccurs 约束

您将在这里检查的最后一个不受支持的 XSD 约束是 maxOccurs。在该示例中(请参见清单 15)中,父元素 PellAwardResponseType 具有一个名为 FSACode 的子元素,此子元素应该出现零至三次。

清单 15. 带 maxOccurs 约束的 XSD
<xs:element name="FSACode" minOccurs="0" maxOccurs="3">
 <xs:simpleType>
  <xs:restriction base="xs:string">
   <xs:enumeration value="SA"/>
   <xs:enumeration value="CE"/>
   <xs:enumeration value="PO"/>
  </xs:restriction>
 </xs:simpleType>
</xs:element>

生成的 Java 代码(请参见清单 16)将 FSACode 表示为字符串列表。它没有将该列表的长度限制为少于四个项目。

清单 16. 为带 maxOccurs 约束的 XSD 生成的 Java 代码
public class PellAwardResponseType
 extends ResponseType
{
 ….
 @XmlElement(name = "FSACode")
 protected List<String> fsaCode;

结束语

JAX-WS 是在 JAX-RPC 和 JAX-WS 基础上进行的重大改进,并将随着时间推移和新产品版本的推出而不断保持改进。但是 Java 代码与 XSD 之间的差异仍然存在;这是由于两种技术之间不匹配这个根本性质所决定的。如果您在针对 WebSphere Application Server V6.0 或更早的版本进行开发,那么 JAX-RPC 是唯一可能的解决方案。对于 WebSphere Application Server V6.1 和更高的版本,可以使用 JAX-WS 作为可选的功能包。它产生更好的代码,并使用 Java 注释使得代码更加可读。但是,您仍然必须处理许多约束。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=SOA and web services, Rational
ArticleID=355011
ArticleTitle=使用 IBM Rational Software Architect 在 Java 中处理 XSD
publish-date=11272008