为啥我的 WCF Web 服务在具有不同字段名称的不同命名空间中显示此对象?

Posted

技术标签:

【中文标题】为啥我的 WCF Web 服务在具有不同字段名称的不同命名空间中显示此对象?【英文标题】:Why is my WCF web service presenting this object in a different namespace with different field names?为什么我的 WCF Web 服务在具有不同字段名称的不同命名空间中显示此对象? 【发布时间】:2014-01-08 07:13:51 【问题描述】:

背景:我正在尝试与 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>

像信封状态字段这样的元素名称是自动生成代码中使用的私有变量的名称。公共属性名称与 DocuSign 发送的 xml 匹配。自动生成的代码还使用 XmlTypeAttribute 使用正确的文档名命名空间标记每个对象。因此,通过查看自动生成的代码,我希望我的服务对输入感到满意,但生成的 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 会以这种方式出现?为什么该对象与我从中提取它的参考不同?更重要的是,我可以修复它吗?

【问题讨论】:

【参考方案1】:

令人震惊的是,我在这方面花了多少时间,尝试了多少解决方案,关注了多少链接,以及我读过多少 SO 答案在最终找到之前没有回答我的问题答案是在 SO 上 right here 超过 2 年!

根本问题是DocuSignEnvelopeInformation对象默认被DataContractSerializer序列化和反序列化。这实质上序列化了组成其本地命名空间中的对象的私有成员变量,而不是公共属性。令人生气的是,这是 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 命名空间中(如您在上面的代码示例中所见)。该服务现在可以正确反序列化这些调用。

【讨论】:

以上是关于为啥我的 WCF Web 服务在具有不同字段名称的不同命名空间中显示此对象?的主要内容,如果未能解决你的问题,请参考以下文章

Visual Studio 中的 WCF Web 服务

WCF 项目库

Retrofit2:如何在具有不同字段名称的对象中接收 JSON 响应?

WCF 服务证书和客户端端点身份 - 为啥它不起作用?

WCF Web 服务无法访问 Web Config 中的服务名称

WCF 上的一项服务、多个端点、多个绑定。为啥我无法到达我的端点?