xsd:any:幕后故事

日期: 2008-03-17 作者:Russell Butek 来源:TechTarget中国

  xsd:any 是一个用于设计 XML 模式的流行特性。但它所带来的麻烦常常比它所带来的价值还要多。了解 xsd:any 在 XML 本身中和 JAX-RPC Java 语言绑定中的一些不足之处后,即使您决定使用 xsd:any,也能为更好地使用它做好准备。

  引言

  人们为什么喜欢使用 xsd:any 呢?我听人们谈起过很多原因:为了实现松散耦合、为了启用版本管理、为了获得完全灵活的多态性。而所有这些理由最终都归结为不需对 API 进行更改。这是一个很不错的理由,但在实践中,使用 xsd:any 既不恰当,也不实际。如果所处的是动态环境,仅处理 XML,则 xsd:any 可能非常合适(也的确如此)。但大多数人并不喜欢动态访问 XML(即 SAX 或 DOM 或 SAAJ 编程模型)的复杂性,而且,即使他们喜欢这种复杂性,动态性也会受到局限。而 XML 很少存在于“真空环境”中。XML 几乎总是映射到别的对象或与其他对象进行交互。例如,XML 模式可能作为 Web 服务的一部分而存在,而这意味着将可能处理语言绑定问题。因此,应当对语言绑定将 xsd:any 映射到给定语言的方式加以注意。在本文中,我将重点讨论 JAX-RPC Java™ 语言绑定。

  使用 xsd:any 的 Web 服务示例

  让我们看一下清单 1 中的 WSDL 文件示例。这是一个简单的宠物医院 Web 服务。它定义了一个名为 registerPet 的操作,其输入信息包含一个 xsd:any。此 WSDL 还定义了两个宠物类型:cat 和 fish。

  清单 1. 使用 xsd:any 的兽医服务 WSDL

 <?xml version=”1.0″ encoding=”UTF-8″?>
<definitions
    targetNamespace=”urn:any”
    http://schemas.xmlsoap.org/wsdl/soap/”>http://schemas.xmlsoap.org/wsdl/soap/“
   
    http://schemas.xmlsoap.org/wsdl/”>http://schemas.xmlsoap.org/wsdl/“
    http://schemas.xmlsoap.org/wsdl/”>http://schemas.xmlsoap.org/wsdl/“>
  <types>
    <schema
        targetNamespace=”urn:any”
       
        http://www.w3.org/2001/XMLSchema”>http://www.w3.org/2001/XMLSchema“
        http://www.w3.org/2001/XMLSchema”>http://www.w3.org/2001/XMLSchema“>
      <complexType name=”Cat”>
        <sequence>
          <element name=”name” type=”xsd:string”/>
          <element name=”breed” type=”xsd:string”/>
        </sequence>
      </complexType>
      <complexType name=”Fish”>
        <sequence>
          <element name=”name” type=”xsd:string”/>
          <element name=”saltWater” type=”xsd:boolean”/>
        </sequence>
      </complexType>
      <element name=”registerPet”>
        <complexType>
          <sequence>
            <any namespace=”##any”/>
          </sequence>
        </complexType>
      </element>
      <element name=”registerPetResponse”>
        <complexType>
          <sequence/>
        </complexType>
      </element>
    </schema>
  </types>
  <message name=”registerPetRequest”>
    <part name=”registerPetRequest” element=”tns:registerPet”/>
  </message>
  <message name=”registerPetResponse”>
    <part name=”registerPetResponse” element=”tns:registerPetResponse”/>
  </message>
  <portType name=”Vet”>
    <operation name=”registerPet”>
      <input message=”tns:registerPetRequest”/>
      <output message=”tns:registerPetResponse”/>
    </operation>
  </portType>
  <binding name=”VetSOAP” type=”tns:Vet”>
    <soap:binding style=”document”
        transport=”http://schemas.xmlsoap.org/soap/http”/>
    <operation name=”registerPet”>
      <soap:operation/>
      <input>
        <soap:body use=”literal”/>
      </input>
      <output>
        <soap:body use=”literal”/>
      </output>
    </operation>
  </binding>
  <service name=”VetService”>
    <port name=”VetSOAP” binding=”tns:VetSOAP”>
      <soap:address location=”http://localhost:9080/VetService/services/Vet”/>
    </port>
  </service>
</definitions>

  registerPet 的 xsd:any 参数可以为以下二者之一:

  Cat 或 Fish 的实例

  在其他位置或别的时候定义的其他宠物类型的实例。

  xsd:any 背离了 WSDL 的用途

  请注意,上面列出的两个参数并未在 WSDL 中定义。我自己确定其定义。WSDL 即 Web 服务描述语言 (Web Services Description Language)。其主要的用途就是尽可能完整地描述服务的接口。当使用 xsd:any 时,您就背离了 WSDL 的此用途。

  第一个项目符号声明该参数将为 cat 或 fish。但 WSDL 并不告诉您这一点。它所标明的是该参数可以为任何事物。考虑到这是一个兽医服务,则可能不会处理订单或保险索赔或税收报表——但您可能恰恰会这样猜想;WSDL 并没有告诉您这些信息。

  处理此伪多态性的一个更好方法是定义真正的多态性。定义一个基类型,称为 Pet,然后重新编写 Cat 和 Fish 来对 Pet 进行扩展。

  第二个项目符号声明其他类型将在其他地方或别的时候进行定义。这表明这个 WSDL 并不是此服务的完整接口。这可能会让人觉得可以更改服务并保持 API 不变,但由于此 WSDL 不是完整的 API,这样的说法就不成立了。对于使用此 WSDL 的客户机,要成功地与此服务进行通信,仅这个 WSDL 是不够的。客户是否可以将短吻鳄 (alligator) 送来接受服务?只通过查看 WSDL,您不会知道答案(我认识的可以给短吻鳄看病的兽医也不多!)。您需要一些更多的带外定义。

  JAX-RPC 与 xsd:any 之间的关系不是特别友好

  最后,为了使用此服务,您必须将此 WSDL 映射到某个语言绑定。我将讨论一下 JAX-RPC 规范方面的情况。根据 JAX-RPC,xsd:any 映射到 javax.xml.soap.SOAPElement,而后者是在 SAAJ 规范中定义的类型。SAAJ 是与 DOM 类似的 API,用于访问 SOAP 消息中的 XML 实例。SAAJ 是一种用于访问 XML 实例数据的非常动态的低级别方法,不是特别友好。请参见清单 2,以查看清单 1 中的 WSDL 的 Java 接口。

  清单 2. 使用 xsd:any 的 WSDL 的 Java 接口

package any;

public interface Vet extends java.rmi.Remote {
    public void registerPet(javax.xml.soap.SOAPElement any)
        throws java.rmi.RemoteException;
}
 
  对于动态程序员而言,这可能是个非常好的映射,但我们其他人却宁愿使用生成的 Cat 和 Fish 类,也不愿使用 SOAPElement。如果可以直接将一个 Cat 或 Fish 放入 SOAPElement 中,这样也挺好,但没有特定于 SAAJ 的方式来进行此操作。JAX-RPC 规定 xsd:any 映射到 SOAPElement,但仍然未能提供用于在类实例和 SOAPElement 之间进行转换的方法。

  如果您习惯使用 DOM、SAX 或 SAAJ,则 xsd:any 对您而言就不是问题。不过您需要考虑一下您的 WDSL 的使用者的情况。仅由于您习惯这种级别的编程,您的 WSDL 的其他使用者是否也对此很熟悉呢?

  关于 xsd:anyType 的一些思考

  xsd:any 的一个替代方法是 xsd:anyType(请参阅清单 3 中的 WSDL——与清单 1 的不同之处在于使用粗体突出显示——和清单 4 中的 Java 接口)。xsd:anyType 的缺点在于 JAX-RPC 未为其定义映射。因此,即使有供应商映射此 XML 类型,您仍然无法编写使用该类型的可移植代码。虽然这样说,此类型仍然有一个优势,即一些供应商(如 IBM)将其映射到了 java.lang.Object 之类的有用类型。对于 WebSphere Application Server 的 SOAP 引擎,如果方法接受 java.lang.Object 类型的参数,则可以将任何 Java 对象发送到该方法,而且,只要该对象是符合 XML 标准的对象,就可以将其序列化到 SOAP 消息中。另一方面,如果接收端 SOAP 引擎知道如何将 XML 实例反序列化为 Java 对象,它就将进行此操作。如果引擎不知道如何反序列化该对象,接收方将获得一个 SOAPElement,和使用 xsd:any 时一样。

  请记住,尽管 xsd:anyType 看起来比 xsd:any 更好用,但缺乏 JAX-RPC 映射支持。只有在以下情况它才具有优势:

  您不担心代码是不可移植的。

  服务实现和该服务对应的客户机实现的基础平台具有针对 xsd:anyType 的某种映射。

  清单 3. 使用 xsd:anyType 的兽医服务 WSDL

<?xml version=”1.0″ encoding=”UTF-8″?>
<definitions
    targetNamespace=”urn:any”
    http://schemas.xmlsoap.org/wsdl/soap/”>http://schemas.xmlsoap.org/wsdl/soap/“
   
    http://schemas.xmlsoap.org/wsdl/”>http://schemas.xmlsoap.org/wsdl/“
    http://schemas.xmlsoap.org/wsdl/”>http://schemas.xmlsoap.org/wsdl/“>
  <types>
    <schema
        targetNamespace=”urn:any”
       
        http://www.w3.org/2001/XMLSchema”>http://www.w3.org/2001/XMLSchema“
        http://www.w3.org/2001/XMLSchema”>http://www.w3.org/2001/XMLSchema“>
      <complexType name=”Cat”>
        <sequence>
          <element name=”name” type=”xsd:string”/>
          <element name=”breed” type=”xsd:string”/>
        </sequence>
      </complexType>
      <complexType name=”Fish”>
        <sequence>
          <element name=”name” type=”xsd:string”/>
          <element name=”saltWater” type=”xsd:boolean”/>
        </sequence>
      </complexType>
      <element name=”registerPet”>
        <complexType>
          <sequence>
            <element name=”pet” type=”xsd:anyType”/> 
          </sequence>
        </complexType>
      </element>
      <element name=”registerPetResponse”>
        <complexType>
          <sequence/>
        </complexType>
      </element>
    </schema>
  </types>
  <message name=”registerPetRequest”>
    <part name=”registerPetRequest” element=”tns:registerPet”/>
  </message>
  <message name=”registerPetResponse”>
    <part name=”registerPetResponse” element=”tns:registerPetResponse”/>
  </message>
  <portType name=”Vet”>
    <operation name=”registerPet”>
      <input message=”tns:registerPetRequest”/>
      <output message=”tns:registerPetResponse”/>
    </operation>
  </portType>
  <binding name=”VetSOAP” type=”tns:Vet”>
    <soap:binding style=”document”
        transport=”http://schemas.xmlsoap.org/soap/http”/>
    <operation name=”registerPet”>
      <soap:operation/>
      <input>
        <soap:body use=”literal”/>
      </input>
      <output>
        <soap:body use=”literal”/>
      </output>
    </operation>
  </binding>
  <service name=”VetService”>
    <port name=”VetSOAP” binding=”tns:VetSOAP”>
      <soap:address location=”http://localhost:9080/VetService/services/Vet”/>
    </port>
  </service>
</definitions>

  清单 4. 使用 xsd:anyType 的 WSDL 的 Java 接口

package any;

public interface Vet extends java.rmi.Remote {
    public void registerPet(java.lang.Object pet)
        throws java.rmi.RemoteException;
}
 
  总结

  尽管在 Web 服务的 XML 模式中使用 xsd:any 似乎是个不错的主意,但这样做存在一些缺陷:您将背离 WSDL 的用途,而 xsd:any 的语言映射可能不便于使用。虽然我并不会说使用 xsd:any 是一种不好的做法,但我希望这个提示能在出现问题前让您对即将出现的问题有更好的认识。

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

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

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

微信公众号

TechTarget微信公众号二维码

TechTarget

官方微博

TechTarget中国官方微博二维码

TechTarget中国

相关推荐