Web服务提示和技巧:避免匿名类型

日期: 2011-11-28 作者:Russell ButekShannon Kendrick 来源:TechTarget中国 英文

  我们经常看到由匿名类型编写的Web服务XML架构。此架构的作者在构建这类架构时也不太可能会意识到匿名类型可能会导致潜在问题。本文将总结使用匿名类型可能会引起的问题,希望读者能够避免它们。

  什么是匿名类型?

  匿名XML类型是xsd:element中嵌入的类型。由于它嵌入到xsd:element中,因此它没有名称。比如,参见清单1 。

  清单1. 匿名“person”类型
    

以下是引用片段:
<xsd:element name=”person”>
  <xsd:complexType>
    <xsd:sequence>
      <xsd:element name=”first” type=”xsd:string”/>
      <xsd:element name=”last” type=”xsd:string”/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>

  名为“person”的元素中的complexType本身没有名称,因此它是匿名的。可以按照以下步骤将这个匿名类型转换为命名类型:

  将complexType定义移到全局域中。

  为complexType提供一个名称属性。

  为全局元素提供一个类型属性,该属性引用新命名的complexType。

  按照以下步骤,将清单1中的匿名类型转换为清单2中的命名类型。

  清单2. 命名的Person类型
            

以下是引用片段:
<xsd:element name=”person” type=”Person”/>
<xsd:complexType name=”Person”>
  <xsd:sequence>
    <xsd:element name=”first” type=”xsd:string”/>
    <xsd:element name=”last” type=”xsd:string”/>
  </xsd:sequence>
</xsd:complexType>

  清单1和清单2中的架构在逻辑上是一致的。它们都能用于验证清单3中的XML实例。

  清单3. 命名的Person类型
               

以下是引用片段:
<person>
     <first>John</first>
     <last>Doe</last>
</person>

  匿名类型有什么问题?

  我们将在下面使用清单4和5中的架构。清单4没有定义一个命令类型。所有类型都是匿名的。为了进行比较,清单5显示了具有已命名类型的等价架构。我们将上面的步骤应用到清单4以创建清单5。

  清单4. 使用匿名类型定义的account元素
                   

以下是引用片段:
<xsd:element name=”account”>
  <xsd:complexType>
    <xsd:sequence>
      <xsd:element name=”number” type=”xsd:int”/>
      <xsd:element name=”person”>
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name=”firstName” type=”xsd:string”/>
            <xsd:element name=”lastName” type=”xsd:string”/>
            <xsd:element name=”otherNamesOnAccount” minOccurs=”0″ maxOccurs=”unbounded”>
              <xsd:complexType>
                <xsd:sequence>
                  <xsd:element name=”person”>
                    <xsd:complexType>
                      <xsd:sequence>
                        <xsd:element name=”firstName” type=”xsd:string”/>
                        <xsd:element name=”lastName” type=”xsd:string”/>
                      </xsd:sequence>
                    </xsd:complexTypeb>
                  </xsd:element>
                  <xsd:element name=”relationshipToAccountOwner” type=”xsd:string”/>
                </xsd:sequence>
              </xsd:complexType>
            </xsd:element>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>

  清单5. 使用已命名类型定义的account元素   
                 
以下是引用片段:
<xsd:element name=”account” type=”tns:Account”/>
<xsd:complexType name=”Account”>
  <xsd:sequence>
    <xsd:element name=”number” type=”xsd:int”/>
    <xsd:element name=”person” type=”tns:AccountOwner”/>
  </xsd:sequence>
</xsd:complexType>
<xsd:complexType name=”AccountOwner”>
  <xsd:sequence>
    <xsd:element name=”firstName” type=”xsd:string”/>
    <xsd:element name=”lastName” type=”xsd:string”/>
    <xsd:element name=”otherNamesOnAccount” minOccurs=”0″ maxOccurs=”unbounded”
        type=”tns:OtherNameOnAccount”/>
  </xsd:sequence>
</xsd:complexType>
<xsd:complexType name=”OtherNameOnAccount”>
  <xsd:sequence>
    <xsd:element name=”person” type=”tns:Person”/>
    <xsd:element name=”relationshipToAccountOwner” type=”xsd:string”/>
  </xsd:sequence>
</xsd:complexType>
<xsd:complexType name=”Person”>
  <xsd:sequence>
    <xsd:element name=”firstName” type=”xsd:string”/>
    <xsd:element name=”lastName” type=”xsd:string”/>
  </xsd:sequence>
</xsd:complexType>

  我们为何不喜欢清单4中的匿名类型?

  不能重用

  还是必须对匿名类型进行命名。

  清单4没有清单5简洁。

  本文其余部分将详细讨论这三点。

  不能重用

  这是匿名类型的主要问题。当在一个元素中以匿名方式定义某个类型时,只有那个元素采用此类型。如果您希望别处的另一个元素也采用相同的类型,最好的方法是将该匿名类型剪切并粘贴到新元素中。

  例如,在清单4复制元素的firstName/lastName 对,其中一个位于外部 person 元素中,而另一个位于内部person元素中。要支持重用,必须将这两个元素放置到它们各自的命名类型中。如果没有这样的命名类型,这些元素只是从架构的一个点复制到另一个点。

  注意,现在在清单5中我们拥有这样一个命名类型:Person。清单5是清单4的命名等价物;我们没有执行任何会影响XML实例结构的更改。但现在我们拥有了一个Person命名类型,我们就可以在AccountOwner中重用Person类型以进一步改进清单5中的架构。

  还是必须对匿名类型进行命名

  即使您并不关心重用,使用匿名类型仍然存在潜在的危害。考虑一下 XML 架构是如何被使用的。例如,在一个 Web 服务定义中,XML 架构很有可能会映射到一种编程语言。如果这样,那么在该语言中,类型可能不再是匿名的。它们必须以某种方式命名,而且名称必须由周围的架构派生而来。无论您对其采用什么派生规则,都可能出现打破那些规则的场景。

  例如,JAXB规范中用于将XML架构映射到Java的规则会在清单4中定义的架构中发生故障。JAXB定义了,匿名类型的类必须采用一个源自容器元素的名称。此外,XML中的嵌入式匿名复杂类型变成Java中的内部类。根据JAXB,清单4中的XML架构将映射到下面的Java类:

以下是引用片段:
Account 
Account.Person 
Account.Person.OtherNamesOnAccount 
Account.Person.OtherNamesOnAccount.Person 

  Java不允许嵌套类与容器类共享名称。否则将会导致一个编译时间错误,如 “该嵌套类型Person无法隐藏封闭类型”。

  针对这个问题,我们采取的解决办法是命名所有类型(见清单 5)。您可以看到,我们主要采用JAXB使用的名称派生规则 ,即从封装元素的名称中派生出 Java 类型的名称。但我们没有对所有类型采用这种方法,否则将会得到两个“Person”类型。因此,我们将第一个“Person” 类型随意命名为“AccountOwner”。

  JAXB绑定文件

  尽管我们喜欢这个解决办法,但该办法需要更改XML架构。对于那些无法更改架构的情况,JAXB提供了另一种解决方案。JAXB允许用户编写一个绑定文件,以告知JAXB引擎如何将XML映射到Java。JAXB规范的作者知道,一个指定映射并不总能适合每种情况,因此他们提供了这种机制来偏离标准。清单6中的绑定文件告知JAXB生成器修改从第一个 person 元素之下的匿名 complexType 所生成的类,以便将其命名为“AccountOwner”。

  清单6. 修复清单4的问题所需的JAXB绑定文件
     

以下是引用片段:
<jaxb:bindings 
 
mlns:xsd=”http://www.w3.org/2001/XMLSchema”
xsi:schemaLocation=”http://java.sun.com/xml/ns/jaxb 
http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd”
  jaxb:version=”2.1″>
  <jaxb:bindings schemaLocation=”Anonymous.xsd” node=”/xsd:schema
    /xsd:element[@name=’account’]/xsd:complexType/xsd:sequence
    /xsd:element[@name=’person’]/xsd:complexType”>
    <jaxb:class name=”AccountOwner”/>
  </jaxb:bindings>
</jaxb:bindings>

  内部jaxb:bindings元素包含一个schemaLocation属性,告知引擎这个绑定文件影响哪个架构;它包含一个节点属性,该节点属性的值是一个要应用这个特殊绑定的元素中的一个XPath表达式。接着jaxb:class元素会告诉您,从给定XPath的元素所生成的JAXB类将被命名为“AccountOwner”。要详细了解JAXB映射文件。

  JAXB非常稳健,可允许您通过绑定文件来命名需要命名的匿名类型。但它增加了服务构建流程的难度。您不能依赖所有语言映射来获得这种级别的功能。因此,如果您可以自由修改架构,最好避免匿名类型。

  不够简洁

  最后,我们认为清单4没有清单5简洁。我们承认,这个最后的原因完全是主观判断。但我们认为,匿名类型没有命名类型那样的可读性;深层嵌套的XML结构通常难以理解。讨论匿名类型结构比讨论命名类型结构更加困难。

  结束语

  基于以下几个原因,应该避免使用匿名类型:

  它们不允许重用;

  当映射必须命名匿名类型时,它们可能会引起问题;

  它们不如命名类型简洁。

  可以通过将匿名类型转换为命名类型来解决这些问题。对于JAXB从XML架构映射到Java的情况,如果不能更改架构,可以向JAXB引擎提供一个JAXB绑定文件,以减轻可能遇到的问题。

我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。

我原创,你原创,我们的内容世界才会更加精彩!

【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】

微信公众号

TechTarget微信公众号二维码

TechTarget

官方微博

TechTarget中国官方微博二维码

TechTarget中国

相关推荐