与存取元素相关的另一个问题是由于类型关系引起的可代换性。由于前面的adjustment类不是一个final类型的类,transfer对象的from和to域实际引用继承类型的实例是可能的。为了支持这种类型兼容的替换,SOAP使用一个名域限定的类型属性的XML Schema约定。这种类型属性的值是一个对元素具体的类型的限制的名字。考虑下面的adjustment扩展类:
package com.bofsoap.IBank; public class auditedadjustment extends adjustment { public int auditlevel; } |
给出下面Java语言:
transfer xfer = new transfer(); xfer.from = new auditedadjustment(); xfer.from.account = 3514; xfer.from.amount = -100; xfer.from.auditlevel = 3; xfer.to = new adjustment(); xfer.to.account = 3518; xfer.from.amount = 100; |
在SOAP中transfer对象的序列化形式如下所示:
<t:transfer xmlns:xsd=''http://www.w3.org/1999/XMLSchema'' xmlns:t=''urn:develop-com:java:com.bofsoap.IBank''> <from xsd:type=''t:auditedadjustment'' > <account>3514</account> <amount>-100.0</amount> <auditlevel>3</auditlevel > </from> <to> <account>3518</account> <amount>100.0</amount> </to> </t:transfer> |
在这里xsd:type属性引用一个名域限定的类型名,它能被反序列化程序用于实例化对象的正确类型。因为to存取元素引用到一个被预料的类型的实例(而不是一个可代替的继承类型),xsd:type属性是不需要的。
刚才的transfer类设法回避了一个关键问题。如果正被序列化的transfer对象用下面这种方式初始化将会发生什么情况:
transfer xfer = new transfer(); xfer.from = new adjustment(); xfer.from.account = 3514; xfer.from.amount = -100; xfer.to = xfer.from; |
基于以前的议论,在SOAP 中transfer对象的序列化形式如下所示:
<t:transfer xmlns:t=''urn:develop-com:java:com.bofsoap.IBank''> <from> <account>3514</account> <amount>-100.0</amount> </from> <to> <account>3514</account> <amount>-100.0</amount> </to> </t:transfer> |
这个表达有两个问题。首先最容易理解的问题是同样的信息被发送了两次,这导致了一个比实际所需要消息的更大的消息。一个更微妙的但是更重要的问题是由于反序列化程序不能分辨两个带有同样值的adjustment对象与在两个地方被引用的一个单一的adjustment对象的区别,两个存取元素间的身份关系就被丢失。如果这个消息接收者已经在结果对象上执行了下面的测试,(xfer.to == xfer.from)将不会返回true。
void processTransfer(transfer xfer) { if (xfer.to == xfer.from) handleDoubleAdjustment(xfer.to); else handleAdjustments(xfer.to, xfer.from); } |
为了支持必须保持身份关系的类型的序列化,SOAP支持多引用存取元素。目前我们接触到的存取元素是单引用存取元素,也就是说,元素值是嵌入在存取元素下面的,而且其它存取元素被允许引用那个值(这很类似于在NDR中的[unique]的概念)。多引用存取元素总是被编码为只包含已知的soap:href属性的空元素。soap:href属性总是包含一个代码片段标识符,它对应于存取元素引用到的实例。如果to和from存取元素已经被编码为多引用存取元素,序列化的transfer对象如下所示:
<t:transfer xmlns:t=''urn:develop-com:java:com.bofsoap.IBank''> <from soap:href=''#id1'' /> <to soap:href=''#id1'' /> </t:transfer> |
这个编码假设与adjustment类兼容的一个类型的实例已经在envelope中的其它地方被序列化,而且这个实例已经被用soap:id属性标记,如下所示:
<t:adjustment soap:id=''id1''xmlns:t=''urn:develop-com:java:com.bofsoap.IBank''> <account>3514</account> <amount>-100.0</amount> </t:adjustment> |
第四节 结语
一个遗留的HTTP问题还需要进一步阐明。SOAP支持(但不需要)HTTP扩展框架约定来指定必须的HTTP头扩展。这些约定主要有两个目的。首先,它们允许任意的URI被用于限定给定的HTTP头的范围(类似XML名域)。第二,这些约定允许把必须的头与可选的头区分开来(象soap:mustUnderstand)。下面是一个使用HTTP扩展框架来把SOAPMethodName头定义成为一个必须的头扩展:
M-POST /foobar HTTP/1.1 Host: 209.110.197.2 Man: "urn:schemas-xmlsoap-org:soap.v1; ns=42" 42-SOAPMethodName: urn:bobnsid:IFoo#DoIt |
Man头映射SOAP URI到前缀为42的头,并表示没有认出SOAP的服务器必须返回一个HTTP错误,状态代码为501 (没有被实现) 或 510 (没有被扩展)。HTTP方法必须是M-POST,表明目前是必须的头扩展。SOAP是一个被类型化的序列化格式,它恰巧用HTTP 作为请求/响应消息传输协议。SOAP被设计为与正将出现的XML Schema规范密切配合,并支持在Internet的任何地方运行的COM、CORBA、Perl、Tcl、和Java、C、Python或 PHP等程序间的互操作性。