内容


Web 服务技巧

将多态性作为 xsd:choice 的备选方法

当不能使用 xsd:choice 的映射时可以采用何种措施

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: Web 服务技巧

敬请期待该系列的后续内容。

此内容是该系列的一部分:Web 服务技巧

敬请期待该系列的后续内容。

引言

xsd:choice 是常见的 XML 构造。不过,并不能总是方便地将 xsd:choice 映射到编程语言。例如,JAX-RPC 规范所定义的 Web 服务的 Java 映射并不提供从 xsd:choice 到 Java 的显式映射。只要 JAX-RPC 代码生成器在类型定义中遇到 xsd:choice,就会将该类型映射为 javax.xml.soap.SOAPElementSOAPElement 属于 SAAJ API,不是一个非常用户友好的 API。

可以进行一些工作,以获得更好的 API。如果可以更改 XML 模式,则一个不错的选择就是使用多态性替代 xsd:choice

xsd:choice 与多态性的对比

多态性表示具有“很多种形式”。例如,一个方法参数可以使用基类型进行声明。当调用此方法时,该参数的实例将为该类型的特定扩展。

xsd:choice 也可以进行类似的描述。可以使用描述所有可能 choice 的类型对方法参数进行声明。调用该方法时,该参数的实例将包含一个特定的 choice。

虽然实际的说法不同,但意思实质上却是一样的。

将简单 xsd:choice 类型映射到多态类型

为了将典型的 choice 类型转换为一个多态类型集,需要进行以下操作(假定 choice 类型 C 中包含的选择为 c1..cn):

  1. 构建一个抽象类 P,其中包含 C 的这些元素,但这些元素不是 choice 元素。
  2. 对于 c1..cn 中的每个 c,均为其构建一个扩展类型 E of P,该类型中包含 c 的元素。

通过示例理解概念总是更容易一些。让我们来看看 payment 选项的声明。清单 1 显示了一个 choice 类型。而清单 2 显示了转换为多态类型集的声明。

清单 1. choice 型 payment
<complexType name="Payment">
  <sequence>
    <element name="amount" type="int"/>
    <choice>
      <element name="cash" nillable="true" type="string"/>
      <element name="check" type="tns:Check"/>
      <element name="credit" type="tns:Credit"/>
    </choice>
  </sequence>
</complexType>

<complexType name="Check">
  <sequence>
    <element name="checkNo" type="int"/>
  </sequence>
</complexType>
<complexType name="Credit">
  <sequence>
    <element name="cardNo" type="string"/>
    <element name="expiration" type="date"/>
  </sequence>
</complexType>
清单 2. 多态型 payment
<complexType abstract="true" name="Payment">
  <sequence>
    <element name="amount" type="int"/>
  </sequence>
</complexType>

<complexType name="CashPayment">
  <complexContent>
    <extension base="tns:Payment">
      <sequence>
        <element name="cash" nillable="true" type="string"/>
      </sequence>
    </extension>
  </complexContent>
</complexType>

<complexType name="CheckPayment">
  <complexContent>
    <extension base="tns:Payment">
      <sequence>
        <element name="check" type="tns:Check"/>
      </sequence>
    </extension>
  </complexContent>
</complexType>

<complexType name="CreditPayment">
  <complexContent>
    <extension base="tns:Payment">
      <sequence>
        <element name="credit" type="tns:Credit"/>
      </sequence>
    </extension>
  </complexContent>
</complexType>

多态型 payment 示例比 choice 型 payment 示例更为冗长。这是因为 XML 不是真正的面向对象的编程语言,不能直接向其应用面向对象的功能。但这不应该对您有所影响。您很有可能会将 XML 映射到某种编程语言。作为使用该特定语言的开发人员,您不需要理会 XML 模式的样子,而只需要关心模式的语言映射的情况。

只有进行分析的时候,才应该了解 XML 模式的情况。例如,如果此模式是 Web 服务的一部分,而您希望比较这些类型的简单对象访问协议 (SOAP) 消息,从而确定消息大小本身是否影响性能。那么,让我们对这两个变体的实例进行比较。例如,假设我们对于金额为 10 美元的交易以支票付帐 (number 1050)。清单 3 显示了 choice 示例,而清单 4 显示了多态示例。(请注意,为了简化示例,我忽略了命名空间和前缀。)

清单 3. choice 型 payment 实例
<payment>
  <amount>10</amount>
  <check>
    <checkNo>1050</checkNo>
  </check>
</payment>
清单 4. 多态型 payment 实例
<payment xsi:type="CheckPayment">
  <amount>10</amount>
  <check>
    <checkNo>1050</checkNo>
  </check>
</payment>

正如您所看到的,清单 4 与清单 5 的唯一区别在于多态实例中包含类型信息。尽管这一差别值得注意,但通常并不重要。事实上,这可能会潜在地简化 SOAP 工程师的处理,因为 SOAP 工程师从一开始就知道此类型的情况,能够事先就选择恰当的反序列程序,而不用对实例进行分析和监视 choice 元素(在此例中为 check)的名称以确定类型。

因此使用多态方法并非比使用 choice 方法的开销大,而且可以提供更具用户友好性的语言映射。

对 maxOccurs 大于 1 的 xsd:choice 进行映射

大部分 choice 场景都与上面所示的例子相似。但 choice 可能具有大于 1maxOccurs 属性。此映射过程相似,但需要进行额外的工作,以处理此新类型的新数组特性。

  1. 构建一个空的抽象类型 P
  2. 使用 P 类型的元素替代 C 的 choice,且同时将 maxOccurs 属性从 choice 带到新元素中。
  3. 对于 c1..cn 中的每个 c,均为起构建一个 P 的扩展类型 E,该类型中包含 c 的元素。

例如,对清单 1 中的 Payment 类型进行修改,使其在 choice 上包含一个 maxOccurs="unbounded" 属性。(请参阅清单 5,添加的部分用粗体突出显示。)

(此类型表示可以使用多个支付选项支付单笔款项:一部分用现金,一部分用支票,一部分用信用卡;或者任意组合。)

清单 5. 具有 maxOccurs 的 choice 型 payment
<complexType name="Payment">
  <sequence>
    <element name="amount" type="int"/>
    <choice maxOccurs="unbounded">
      <element name="cash" nillable="true" type="string"/>
      <element name="check" type="tns:Check"/>
      <element name="credit" type="tns:Credit"/>
    </choice>
  </sequence>
</complexType>

清单 6 显示了转换后的多态类型集。与清单 2 相比,所做的更改用粗体突出显示。

清单 6. 具有 maxOccurs 的多态 payment
<complexType name="Payment">
  <sequence>
    <element name="amount" type="int"/>
    <element name="option" type="PaymentOption" maxOccurs="unbounded"/>
  </sequence>
</complexType>

<complexType abstract="true" name="PaymentOption">
  <sequence/>
</complexType>


<complexType name="CashPayment">
  <complexContent>
    <extension base="tns:PaymentOption">
      <sequence>
        <element name="cash" nillable="true" type="string"/>
      </sequence>
    </extension>
  </complexContent>
</complexType>

<complexType name="CheckPayment">
  <complexContent>
    <extension base="tns:PaymentOption">
      <sequence>
        <element name="check" type="tns:Check"/>
      </sequence>
    </extension>
  </complexContent>
</complexType>

<complexType name="CreditPayment">
  <complexContent>
    <extension base="tns:PaymentOption">
      <sequence>
        <element name="credit" type="tns:Credit"/>
      </sequence>
    </extension>
  </complexContent>
</complexType>

请注意,PaymentOption 没有自身的字段。这或许有些奇怪,但这样并没有任何错误。它是抽象类型,因此永远不会存在 PaymentOption 实例,而只能存在其扩展。

请注意,还可以对典型的 choice 示例应用 maxOccurs 规则。但我希望您能够了解典型示例的简化规则的好处:少了一级需要进行处理的间接寻址。

多态性与 xsd:choice 的优势对比

xsd:choice 相比,多态性具有两个优势:扩展性和类型指示。

扩展性

就其本质而言,多态类型是可扩展的。抽象基类型可以具有任意数量的扩展,具体取决于是出于组织目的而将这些扩展分布到若干 .xsd 文件(一个组织可能仅接受现金和支票;而另一个组织可能仅接受信用卡和在线支付),还是随着时间推移会添加新扩展。

xsd:choice 本身是不可扩展的。如果希望以后不断添加 choice,或希望在不同 .xsd 文件中包含不同的 chioce 集,则要么在一个单态类型中定义所有可能的 choice,而让代码负责处理对于每种场景哪些可用,要么让很多类型包含相同的 choice 选项。这两种方法都不特别可靠。尽管可以扩展 choice 类型,并向扩展类型添加 choice,但为何不再向前迈一步,完全采用多态模式,而删除所有使用 xsd:choice 的地方呢?

类型指示

在这个 payment 示例中,其中一个 choice 就是 cash。不知您是否注意到这是一个采用相当奇怪的方式定义的 choice?对于现金,除了必须知道数量之外,还需要知道别的什么?什么都不用知道了。而这正是添加 nillable="true" 属性的原因;可以对 cash 元素进行赋值,但这没有意义。现金支付的实例可能与清单 7 所示类似。

清单 7. choice 型现金支付实例
<payment>
  <amount>10</amount>
  <cash xsi:isNil="true"/>
</payment>

此处实际上是想指出这是现金支付。但在 choice 中,如果没有占位符就不能指示这一点。

在清单 2 中的 CashPayment 类型中,我一并提供了 cash 元素,因为我直接应用了转换规则,但并非真的需要这个元素。实际上,所需的就是指示此为一个 cash 类型,没有别的了,而多态性的特性就能够做到这一点。因此,CashPayment 选项将扩展 Payment 类型,但却不会添加自身的新内容。清单 8 显示了此类型的一个实例。

清单 8. 改进后的多态 CashPayment
<complexType name="CashPayment">
  <complexContent>
    <extension base="tns:Payment">
      <sequence/>
    </extension>
  </complexContent>
</complexType>
清单 9. 多态现金支付示例
<payment xsi:type="CashPayment">
  <amount>10</amount>
</payment>

总结

在某些场景中,xsd:choice 并非可使用的最佳类型。例如,当使用 JAX-RPC 映射规则将 choice 类型映射到 Java 语言时,最终得到一个 Java 映射,但用户友好性并不好。如果可以控制模式,则可能希望将包含 xsd:choice 的类型转换为一个多态类型集。这将不仅能得到一个更为用户友好的映射,而且多态性比 xsd:choice 更可靠。


相关主题


评论

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=SOA and web services, XML
ArticleID=162678
ArticleTitle=Web 服务技巧: 将多态性作为 xsd:choice 的备选方法
publish-date=12162005