上下文:我正在尝试与DocuSign的Connect通知服务集成。我已经使用一个名为DocuSignConnectUpdate的方法设置了一个WCF服务,该方法将DocuSignEnvelopeInformation作为其唯一参数,由DocuSign指定。此DocuSignEnvelopeInformation对象来自对their API的引用,因此它们可以将此对象传递给我的Web服务,并且我确切地知道会发生什么。 DocuSign要求我在他们的网站上配置我的服务地址和命名空间。
问题: DocuSign发送的XML就是我所期望的。 DocuSignEnvelopeInformation及其子节点位于名称空间“http://www.docusign.net/API/3.0”中,元素名称与对象名称匹配:
<DocuSignEnvelopeInformation xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.docusign.net/API/3.0">
<EnvelopeStatus>...</EnvelopeStatus>
</DocuSignEnvelopeInformation>
但我的Web服务期望在错误的命名空间中使用不同的东西,并且使用修改后的元素名称。这就是我的WSDL中定义DocuSignConnectUpdate方法的方法:
<xs:element name="DocuSignConnectUpdate">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="DocuSignEnvelopeInformation" nillable="true" type="tns:DocuSignEnvelopeInformation"/>
</xs:sequence>
</xs:complexType>
</xs:element>
这就是我在WSDL中定义DocuSignEnvelopeInformation类型的方式:
<xs:complexType name="DocuSignEnvelopeInformation">
<xs:sequence>
<xs:element xmlns:q1="http://schemas.datacontract.org/2004/07/System.ComponentModel" name="PropertyChanged" nillable="true" type="q1:PropertyChangedEventHandler"/>
<xs:element name="documentPDFsField" nillable="true" type="tns:ArrayOfDocumentPDF"/>
<xs:element name="envelopeStatusField" nillable="true" type="tns:EnvelopeStatus"/>
<xs:element name="timeZoneField" nillable="true" type="xs:string"/>
<xs:element name="timeZoneOffsetField" type="xs:int"/>
<xs:element name="timeZoneOffsetFieldSpecified" type="xs:boolean"/>
</xs:sequence>
</xs:complexType>
envelopeStatusField等元素名称是自动生成代码中使用的私有变量的名称。公共属性名称与DocuSign发送的xml相匹配。自动生成的代码还使用XmlTypeAttribute使用正确的docusign命名空间标记每个对象。因此,通过查看自动生成的代码,我希望我的服务对输入感到满意,但生成的WSDL是不同的,如上所示,我的服务无法反序列化xml。
部分代码: DocuSignEnvelopeInformation的自动生成声明:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.17929")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.docusign.net/API/3.0")]
public partial class DocuSignEnvelopeInformation : object, System.ComponentModel.INotifyPropertyChanged {
private EnvelopeStatus envelopeStatusField;
private DocumentPDF[] documentPDFsField;
private string timeZoneField;
private int timeZoneOffsetField;
private bool timeZoneOffsetFieldSpecified;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order=0)]
public EnvelopeStatus EnvelopeStatus {
...
...
OperationContract是唯一的方法:
[SoapHeaders]
[ServiceContract(Namespace = "http:/MyNameSpace")]
public interface IDocusignEventListener
{
[OperationContract]
[FaultContract(typeof(ErrorMessageCollection), Action = Constants.FaultAction)]
string DocuSignConnectUpdate(DocuSignEnvelopeInformation DocuSignEnvelopeInformation);
}
DocuSign调用的方法
[ServiceBehavior(Namespace = "http:/MyNameSpace", ConfigurationName = "DocusignEventListener")]
public class DocusignEventListener : IDocusignEventListener
{
public string DocuSignConnectUpdate(DocuSignEnvelopeInformation DocuSignEnvelopeInformation)
{
...
return DocuSignEnvelopeInformation.EnvelopeStatus.EnvelopeID;
}
}
所以,问题是为什么wsdl会以这种方式出现?为什么对象与我从中提取的引用不同?更重要的是,我能解决它吗?
答案 0 :(得分:11)
令我惊讶的是,我花了多少时间在这上面,我尝试了多少解决方案,我遵循了多少链接,以及在最终找到之前没有回答我的问题的多少SO答案答案是right here在SO上停留超过2年!
根本问题是默认情况下DataContractSerializer正在序列化和反序列化DocuSignEnvelopeInformation对象。这基本上序列化了构成对象的私有成员变量,而不是公共属性。令人高兴的是,这是WCF服务的默认序列化程序。如果服务应用程序的自动生成的代码至少明确标记了[DataContractFormat]
的示例方法,我们将有一个线索可以遵循,但它只是一个不可见的默认值,你必须以某种方式神圣。
解决方案是在界面中使用[XmlSerializerFormat]
标记每个方法。这将DataContractSerializer替换为XmlSerializer作为方法参数的序列化器:
[SoapHeaders]
[ServiceContract(Namespace = "http://www.docusign.net/API/3.0")]
public interface IDocusignEventListener
{
[OperationContract]
[XmlSerializerFormat]
[FaultContract(typeof(ErrorMessageCollection), Action = Constants.FaultAction)]
string DocuSignConnectUpdate(DocuSignEnvelopeInformation DocuSignEnvelopeInformation);
}
就这样,公共属性及其声明的命名空间和我需要的一切现在都被序列化而不是私有数据!
对于我特别关注的是,要接收来自DocuSign的Connect通知服务的电话,我仍然有一个小的命名空间问题。根级参数DocuSignEnvelopeInformation仍在方法调用的命名空间中。我不知道为什么。现在,我只是将方法调用本身放在DocuSign API命名空间中(您可以在上面的代码示例中看到)。该服务现在正确地反序列化这些调用。