2

I have a Web Api Controller as shown below

public class HomeController : ApiController
{
    ....
    [HttpPost]
    [Route("api/Add")]
    public IHttpActionResult Add(Employee emp)
    {
        ...
    }
}

 [Serializable]
 public class Employee
 {
     public string Name { get; set; }
 }

The parameters send in the Http Post request gets mapped in the Controller only when the [Serializable] attribute is removed from the Employee class. If I add the attribute, the value for the property Name in the Employee class is always null

Can someone explain whats going wrong, when the model class is marked as [Serializable] ?

Saanthosh
  • 31
  • 8

1 Answers1

3

To understand why this is happening, you need to understand the precise meaning of SerializableAttribute. It means This class can be serialized by serializing its public and private fields (i.e., not its properties). The attribute dates from c# 1.0, back when all properties had explicit backing fields, and was used initially by BinaryFormatter. So, when auto-implemented properties were introduced in c# 3.0, how were they to be serialized?

Not all serializers support [Serializable], but those that do serialize auto-implemented properties by serializing their hidden backing fields by name and value. You don't say what version of Web API you are using, nor the data format (XML or JSON), so you might be using:

  • DataContractSerializer. The data contract serializers support [Serializable], as stated in Types Supported by the Data Contract Serializer. The XML data contract serializer serializes your Employee class as follows:

    <Employee>
        <_x003C_Name_x003E_k__BackingField>My Name</_x003C_Name_x003E_k__BackingField>
    </Employee>
    
  • DataContractJsonSerializer also serializes the backing fields, producing:

    {"<Name>k__BackingField":"My Name"}
    
  • Json.NET will serialize the backing fields only if DefaultContractResolver.IgnoreSerializableAttribute = false:

        var employee = new Employee { Name = "My Name" };
        var json1 = JsonConvert.SerializeObject(employee, new JsonSerializerSettings { ContractResolver = new DefaultContractResolver { IgnoreSerializableAttribute = false } });
        Console.WriteLine(json1);
    

    Also produces {"<Name>k__BackingField":"My Name"}. Without the setting, Json.NET ignores the attribute.

  • XmlSerializer ignores [Serializable].

  • JavaScriptSerializer also ignores [Serializable].

The current version of Web API uses Json.NET and XmlSerializer, but this is configurable. Earlier versions use the data contract serializers or sometimes even JavaScriptSerializer.

Thus, the reason that your Name property is null is that you are using one of the serializers that support [Serializable] and you are posting JSON or XML that does not have the goofy backing field property name.

To work around this problem, you have the following options:

  1. Annotate your class with data contract attributes. These supersede [Serializable] for all serializers that support it:

    [Serializable]
    [DataContract]
    public class Employee
    {
        [DataMember]
        public string Name { get; set; }
    }
    
  2. If you are using XML, be sure you are using XmlSerializer as specified in here.

  3. If you are using JSON and Json.NET in MVC 4 Web API, you can set IgnoreSerializableAttribute = true in Global.asax.cs in Application_Start:

    protected void Application_Start()
    {
        HttpConfiguration config = GlobalConfiguration.Configuration;
        config.Formatters.JsonFormatter.SerializerSettings.ContractResolver =
            new DefaultContractResolver { IgnoreSerializableAttribute = true };
    }
    
  4. Or, just remove [Serializable], you really don't need it for anything other than BinaryFormatter.

dbc
  • 104,963
  • 20
  • 228
  • 340