我们使用WCF和C#为现有的SOAP Web服务构建了一个客户端。最近,Web服务已更新,我们的客户端停止工作。我认为这个问题最好在Yaron Naveh撰写的这篇博客文章中进行了解释 - Interoperability Gotcha: Order of XML Elements。
我会借用Yaron的例子来解答我的问题。最初,wsdl看起来像:
<s:element name="root">
<s:complexType>
<s:sequence>
<s:element name="elem1" type="s:string" />
<s:element name="elem2" type="s:string" />
</s:sequence>
</s:complexType>
</s:element">
WCF生成的代理使用了显式元素排序,如:
[XmlElement(Order=0)]
public string Elem1
{
...
}
[XmlElement(Order=1)]
public string Elem2
{
...
}
在更新中,新元素被添加到类型中,但此元素已添加到序列的中间。
<s:element name="root">
<s:complexType>
<s:sequence>
<s:element name="elem1" type="s:string" />
<s:element name="NewElement" type="s:string" />
<s:element name="elem2" type="s:string" />
</s:sequence>
</s:complexType>
</s:element">
我的WCF代理无法反序列化在添加的NewElement之后排序的任何元素。
Web服务提供商希望此更改向后兼容旧版客户端。显然,我的客户是唯一停止工作的客户。
这是WSDL中的重大变化吗?
是否应将新元素添加到序列的末尾以防止破坏现有客户端?会使这种向后兼容吗?
如果我删除XmlElement属性上的order参数,我的代理会为这样的未来更改做好更充分的准备吗?如果我删除订单,我会放弃什么?
答案 0 :(得分:14)
是的,这是他们的WSDL的重大变化。你是对的,将新元素添加到序列的末尾将使其向后兼容。
如果他们希望消费者接受任何顺序的元素,他们应该使用<xsd:all>
而不是<xsd:sequence>
。当他们添加新元素时,它是必需元素,除非他们在其模式定义中添加minOccurs='0'
属性以使其成为可选元素。
他们还可以通过在序列末尾添加<xsd:any>
元素作为未来元素的占位符,使他们的模式更具前向兼容性。
如果从代理中删除Order,则可能遇到的主要问题是它们是否允许重复序列中具有相同名称的元素:
<s:element name="root">
<s:complexType>
<s:sequence>
<s:element name="elem1" type="s:string" />
<s:element name="elem2" type="s:string" />
<s:element name="elem1" type="s:string" />
</s:sequence>
</s:complexType>
</s:element">
如果没有您的订购指示器,WCF将不知道要映射到哪个属性的元素重复。如果他们从.NET或Java等方式生成XML,则这不太可能成为问题,因为这些通常会将数组和列表转换为包含重复子元素的单个父元素。
使用<xsd:all>
的另一个好处是它不允许重复使用相同名称的元素,从而避免了这个问题。 <xsd:sequence>
具有允许同名的多个元素的“功能”,仅根据其在列表中的位置进行区分。
答案 1 :(得分:0)
向后兼容性的最佳方法是使用[DataContract]和[DataMember]属性声明复杂类型,以及遗留Web服务中的[Serializable]。通过声明为DataContract,无论您的复杂类型添加了多少新字段,您的客户端都将获得正确的反序列化参数。