views:

324

answers:

1

I am attempting to create a wcf service that accepts any input (Action="*") and then deserialize the message after determining its type. For the purposes of testing deserialization I am currently hard-coding the type for the test service.

I get no errors from the deserialization process, but only the outer object is populated after deserialization occurs. All inner fields are null. I can process the same request against the original wcf service successfully.

I am deserializing this way, where knownTypes is a type list of expected types:

DataContractSerializer ser = new DataContractSerializer(new createEligibilityRuleSet ().GetType(), knownTypes);
createEligibilityRuleSet newReq = buf.CreateMessage().GetBody<createEligibilityRuleSet>(ser);

Here is the class and sub-classes of the request object. These classes are generated by svcutil using a top down approach from an existing wsdl. I have tried replacing the XmlTypeAttributes with DataContracts and the XmlElements with DataMembers with no difference. It is the instance of CreateEligibilityRuleSetSvcRequest on the createEligibilityRuleSet object that is null. I have included the request retrieved from the request at the bottom

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("svcutil", "3.0.4506.2152")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://RulesEngineServicesLibrary/RulesEngineServices")]
public partial class createEligibilityRuleSet
{

private CreateEligibilityRuleSetSvcRequest requestField;

/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable = true, Order = 0)]
public CreateEligibilityRuleSetSvcRequest request
{
    get
    {
 return this.requestField;
    }
    set
    {
 this.requestField = value;
    }
}
}

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("svcutil", "3.0.4506.2152")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://RulesEngineServicesLibrary")]
public partial class CreateEligibilityRuleSetSvcRequest : RulesEngineServicesSvcRequest
{

private string requestField;

/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified, Order = 0)]

public string request
{
    get
    {
 return this.requestField;
    }
    set
    {
 this.requestField = value;
    }
}
}

[System.Xml.Serialization.XmlIncludeAttribute(typeof(CreateEligibilityRuleSetSvcRequest))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(ApplyMemberEligibilitySvcRequest))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(CreateCompletionCriteriaRuleSetSvcRequest))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(CopyRuleSetSvcRequest))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(DeleteRuleSetByIDSvcRequest))]
[System.CodeDom.Compiler.GeneratedCodeAttribute("svcutil", "3.0.4506.2152")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://RulesEngineServicesLibrary")]
public partial class RulesEngineServicesSvcRequest : ServiceRequest
{
}

/// <remarks/>
[System.Xml.Serialization.XmlIncludeAttribute(typeof(RulesEngineServicesSvcRequest))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(CreateEligibilityRuleSetSvcRequest))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(ApplyMemberEligibilitySvcRequest))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(CreateCompletionCriteriaRuleSetSvcRequest))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(CopyRuleSetSvcRequest))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(DeleteRuleSetByIDSvcRequest))]
[System.CodeDom.Compiler.GeneratedCodeAttribute("svcutil", "3.0.4506.2152")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://FELibrary")]
public partial class ServiceRequest
{

private string applicationIdField;

/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified, Order = 0)]

public string applicationId
{
    get
    {
 return this.applicationIdField;
    }
    set
    {
 this.applicationIdField = value;
    }
}
}

Request from client comes on Message body as below. Retrieved from Message at runtime.

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:rul="http://RulesEngineServicesLibrary/RulesEngineServices"&gt;
<soap:Header/>
   <soap:Body>
      <rul:createEligibilityRuleSet>
         <request>
            <applicationId>test</applicationId>
            <request>Perf Rule Set1</request>
         </request>
      </rul:createEligibilityRuleSet>
   </soap:Body>
</soap:Envelope>
A: 

The specific problem you're seeing is that DataContract doesn't know how to handle unqualified elements. But XmlSerializer does, and your classes are properly attributed for XmlSerializer.

So you should use XmlSerializer instead of DataContractSerializer to deserialize the body contents:

public static T GetBodyWithXmlSerializer<T>(this Message msg)
{
    var ser = new XmlSerializer(typeof(T));
    T o;
    using (var reader = msg.GetReaderAtBodyContents())
    {
        o = (T)ser.Deserialize(reader);
    }
    return o;
}
alexdej
In the code snippet: DataContractSerializer ser = new DataContractSerializer(new createEligibilityRuleSet ().GetType(), knownTypes);createEligibilityRuleSet newReq = buf.CreateMessage().GetBody<createEligibilityRuleSet>(ser);the 'buf' in this request is a MessageBuffer created from a System.ServiceModel.Channels.Message object passed by wcf.1. I do not see a method 'ReadFromBodyContentsToEnd' on the Message class I am using. Should I be using something else?2. If I exclude the line calling above method, I get an error stating 'There is an Error in the XML document'.
Sorry about that, I fixed the code. To get it working, additionally you need to add the following attribute to your createEligibilityRuleSet class:[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://RulesEngineServicesLibrary/RulesEngineServices")]
alexdej
Thanks a lot! I had figured out the XmlRootAttribute requirement in testing and came to let you know, but you figured that out too. Thanks again. My only question remaining is why I had to add the XmlRoot attribute for this deserialization to work, but these objects were deserialized by the system correctly without it by my 'normal' service?
the XmlRootAttribute is used when you do new XmlSerializer(Type) to specify the name/namespace of the root element. WCF specifies this value in a different way (using XmlSerializer.FromMappings).
alexdej
Can you give an example of how the XmlSerializer.FromMappings work?
Sorry it's a bit involved, but have a look at XmlReflectionImporter.ImportMembersMapping(). You'll probably be able to figure out what you need to do with it. It will give you back an XmlMapping object that you can pass to XmlSerializer.FromMappings().
alexdej