简体   繁体   中英

WCF receives empty model from XML

We have a WCF service with defined class (class was autogenerated from XML using http://xmltocsharp.azurewebsites.net/ ):

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 defined like this:

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

and implementation:

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

but "parameter" in Test function contains NULL objects. I know there is a problem with XML deserialization but can't figure out where exactly.

EDIT: this is the request that I'm sending via Postman

<?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>

SOAP-based bindings unpack the SOAP envelope for you, so you shouldn't need to define the SOAP envelope types. The WebHttpBinding which you say you're using, which doesn't know anything about SOAP, would expect that the contract type (the argument of your service method IService.Test ) would match the entire HTTP body, so I see the logic of what you're doing, however I don't think it's necessary to work against the framework like this.

To fix this scenario do a couple of things:

  • amend your implementation of IService.Test to take an argument of type Notifications
    • string Test(Notifications notifications)
  • amend the service config to specify an HTTP-based SOAP binding, probably BasicHttpBinding
    • <endpoint address="/relativeaddress/" binding="basicHttpBinding" ... />
  • I think a SOAP binding will probably ignore WebInvokeAttribute , but to be on the safe side, remove it. It's superfluous for a SOAP binding because SOAP is always POST.

I don't think you need to delete the Envelope and Body types but they won't do anything if they're not referenced, which is what you want. WCF bindings understand the SOAP envelope natively and don't need it to be specified, it's the content of the SOAP Body element that types need to be provided for.

Your XML sample contains a single instance of Notification under Notifications but the name implies that this child element could be repeating - if it is, I don't think the generated classes will work for you because the Notifications class has a singular Notification property - the generator you linked to has no way to know this so I don't fault it in any way. I added a copy of the Notification element as a sibling and ran it through again - this time it generated a list member:

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

You can use the xsd.exe tool supplied with Visual Studio (I think) to generate classes - see the documentation . I wouldn't be surprised if that web tool is driving this behind the scenes but I'd trust xsd.exe to generate classes that have the best possible compliance with what WCF expects of them.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM