WCF从XML

时间:2018-06-13 09:50:10

标签: c# xml web-services wcf xml-deserialization

我们有一个定义了类的WCF服务(类是使用http://xmltocsharp.azurewebsites.net/从XML自动生成的):

namespace CRMtoQLM.DAL
{
    [Serializable]
    [DataContract(Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
    public class Envelope
    {
    [XmlElement(ElementName = "Body", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
    [DataMember]
    public Body Body { get; set; }
    [XmlAttribute(AttributeName = "soapenv", Namespace = "http://www.w3.org/2000/xmlns/")]
    [DataMember]
    public string Soapenv { get; set; }
    [XmlAttribute(AttributeName = "xsd", Namespace = "http://www.w3.org/2000/xmlns/")]
    [DataMember]
    public string Xsd { get; set; }
    [XmlAttribute(AttributeName = "xsi", Namespace = "http://www.w3.org/2000/xmlns/")]
    [DataMember]
    public string Xsi { get; set; }
}

//[Serializable]
[XmlRoot(ElementName = "Body", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
[DataContract(Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public class Body
{
    [XmlElement(ElementName = "notifications", Namespace = "http://soap.sforce.com/2005/09/outbound")]
    [DataMember]
    public Notifications Notifications { get; set; }
}

//[Serializable]
[XmlRoot(ElementName = "notifications", Namespace = "http://soap.sforce.com/2005/09/outbound")]
[DataContract(Namespace = "http://soap.sforce.com/2005/09/outbound")]
public class Notifications
{
    [XmlElement(ElementName = "OrganizationId", Namespace = "http://soap.sforce.com/2005/09/outbound")]
    [DataMember]
    public string OrganizationId { get; set; }
    [XmlElement(ElementName = "ActionId", Namespace = "http://soap.sforce.com/2005/09/outbound")]
    [DataMember]
    public string ActionId { get; set; }
    [XmlElement(ElementName = "SessionId", Namespace = "http://soap.sforce.com/2005/09/outbound")]
    [DataMember]
    public string SessionId { get; set; }
    [XmlElement(ElementName = "EnterpriseUrl", Namespace = "http://soap.sforce.com/2005/09/outbound")]
    [DataMember]
    public string EnterpriseUrl { get; set; }
    [XmlElement(ElementName = "PartnerUrl", Namespace = "http://soap.sforce.com/2005/09/outbound")]
    [DataMember]
    public string PartnerUrl { get; set; }
    [XmlElement(ElementName = "Notification", Namespace = "http://soap.sforce.com/2005/09/outbound")]
    [DataMember]
    public Notification Notification { get; set; }
    [XmlAttribute(AttributeName = "xmlns")]
    [DataMember]
    public string Xmlns { get; set; }
}

//[Serializable]
[XmlRoot(ElementName = "Notification", Namespace = "http://soap.sforce.com/2005/09/outbound")]
[DataContract(Namespace = "http://soap.sforce.com/2005/09/outbound")]
public class Notification
{
    [XmlElement(ElementName = "Id", Namespace = "http://soap.sforce.com/2005/09/outbound")]
    [DataMember]
    public string Id { get; set; }
    [XmlElement(ElementName = "sObject", Namespace = "http://soap.sforce.com/2005/09/outbound")]
    [DataMember]
    public SObject SObject { get; set; }
}

//[Serializable]
[XmlRoot(ElementName = "sObject", Namespace = "http://soap.sforce.com/2005/09/outbound")]
[DataContract(Namespace = "http://soap.sforce.com/2005/09/outbound")]
public class SObject
{
    [XmlElement(ElementName = "Id", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
    [DataMember]
    public string Id { get; set; }
    [XmlElement(ElementName = "Asset_Account_City__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
    [DataMember]
    public string Asset_Account_City__c { get; set; }
    [XmlElement(ElementName = "Asset_Account_Country__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
    [DataMember]
    public string Asset_Account_Country__c { get; set; }
    [XmlElement(ElementName = "Asset_Account_Name__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
    [DataMember]
    public string Asset_Account_Name__c { get; set; }
    [XmlElement(ElementName = "Asset_Customer_ID__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
    [DataMember]
    public string Asset_Customer_ID__c { get; set; }
    [XmlElement(ElementName = "Expiration_Date__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
    [DataMember]
    public string Expiration_Date__c { get; set; }
    [XmlElement(ElementName = "License__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
    [DataMember]
    public string License__c { get; set; }
    [XmlElement(ElementName = "Reader_Code__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
    [DataMember]
    public string Reader_Code__c { get; set; }
    [XmlElement(ElementName = "Reader_Quantity__c", Namespace = "urn:sobject.enterprise.soap.sforce.com")]
    [DataMember]
    public string Reader_Quantity__c { get; set; }
    [XmlAttribute(AttributeName = "type", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
    [DataMember]
    public string Type { get; set; }
    [XmlAttribute(AttributeName = "sf", Namespace = "http://www.w3.org/2000/xmlns/")]
    [DataMember]
    public string Sf { get; set; }
}
}

IService.cs定义如下:

    [OperationContract]
    [WebInvoke(Method = "POST",
    UriTemplate = "Test",
    RequestFormat = WebMessageFormat.Xml)]
    //[XmlSerializerFormat]
    string Test(Envelope parameter);

和实施:

    public string Test(Envelope parameter)
    {
        return "";
    }

但"参数"在Test函数中包含NULL对象。我知道XML反序列化存在问题,但无法弄清楚究竟在哪里。

编辑:这是我通过邮递员发送的请求

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<notifications xmlns="http://soap.sforce.com/2005/09/outbound">
<OrganizationId>123456</OrganizationId>
<ActionId>123456</ActionId>
<SessionId>123456</SessionId>
<EnterpriseUrl>https://eu8.salesforce.com/</EnterpriseUrl>
<PartnerUrl>https://eu8.salesforce.com/</PartnerUrl>
<Notification>
<Id>123456</Id>
<sObject xsi:type="sf:Asset" xmlns:sf="urn:sobject.enterprise.soap.sforce.com">
 <sf:Id>123456</sf:Id>
 <sf:Asset_Account_City__c>123456</sf:Asset_Account_City__c>
 <sf:Asset_Account_Country__c>123456</sf:Asset_Account_Country__c>
 <sf:Asset_Account_Name__c>123456</sf:Asset_Account_Name__c>
 <sf:Asset_Customer_ID__c>123456</sf:Asset_Customer_ID__c>
 <sf:Expiration_Date__c>123456</sf:Expiration_Date__c>
 <sf:License__c>123456</sf:License__c>
 <sf:Reader_Code__c>123456</sf:Reader_Code__c>
 <sf:Reader_Quantity__c>123456</sf:Reader_Quantity__c>
</sObject>
</Notification>
</notifications>
</soapenv:Body>
</soapenv:Envelope>

1 个答案:

答案 0 :(得分:0)

基于SOAP的绑定为您解包SOAP信封,因此您不需要定义SOAP信封类型。你说你正在使用的WebHttpBinding,它对SOAP一无所知,会期望契约类型(服务方法IService.Test的参数)与整个HTTP体相匹配,所以我看到了你正在做的事情的逻辑,但是我认为不必像这样对抗这个框架。

要解决此问题,请执行以下操作:

  • 修改您的IService.Test实现,以获取Notifications类型的参数
    • string Test(Notifications notifications)
  • 修改服务配置以指定基于HTTP的SOAP绑定,可能是BasicHttpBinding
    • <endpoint address="/relativeaddress/" binding="basicHttpBinding" ... />
  • 我认为SOAP绑定可能会忽略WebInvokeAttribute,但为了安全起见,请将其删除。它对于SOAP绑定来说是多余的,因为SOAP始终是POST。

我认为您不需要删除EnvelopeBody类型,但如果他们没有被引用,他们就不会做任何事情,这就是您想要的。 WCF绑定本身可以理解SOAP信封,并且不需要指定它,它是需要提供类型的SOAP Body元素的内容。

您的XML示例在Notification下包含Notifications的单个实例,但名称暗示此子元素可能会重复 - 如果是,我不认为生成的类可以正常工作因为Notifications类有一个单一的Notification属性 - 你链接到的生成器无法知道这一点所以我不会以任何方式对它进行错误处理。我添加了Notification元素的副本作为兄弟并再次运行 - 这次它生成了一个列表成员:

[XmlElement(ElementName="Notification", Namespace="http://soap.sforce.com/2005/09/outbound")]
public List<Notification> Notification { get; set; }

您可以使用Visual Studio提供的xsd.exe工具(我认为)来生成类 - 请参阅the documentation。如果该网络工具在幕后推动这一点,我不会感到惊讶,但我相信xsd.exe能够生成最符合WCF期望的类。