'HttpClient.ReadAsAsync<T> Returns Empty Object When Using [Serializable]

I'm working with the Microsoft ASP.NET Web API Client Libraries (version 4.0.30506 since I have to run on .NET Framework 4.0) to interface with a .NET web API. I have confirmed that the data is being received fine. However, the object returned from the ReadAsAsync call is unpopulated (not null). After digging around online, I found this SO post (see the answer as well):

HttpClient response ReadAsAsync() doesn't fully deserialize object

Turns out, the objects that I'm sending to the client via JSON are marked [Serializable] and that removing this attribute makes everything work fine (which I've confirmed via testing). However, these objects need the [Serializable] attribute for other scenarios where they're used in other apps, so simply removing the attribute isn't really an option.

My code is shown below. Calls (not shown) to the Get method return an unpopulated Customer object when the [Serializable] attribute is applied to the Customer object (as shown). When the attribute is removed, the Customer object returned is property populated.

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

    public class WebAPIClient
    {
        private readonly HttpClient _httpClient;

        public WebAPIClient(Uri baseAddress)
        {
            _httpClient = new HttpClient();
            _httpClient.BaseAddress = baseAddress:
            _httpClient.DefaultRequestHeaders.Accept.Clear();
            _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        }

        public Customer Get(int id)
        {
            string url = [code that builds url] + id.ToString();
            HttpResponseMessage response = _httpClient.GetAsync(url).Result;
            response.EnsureSuccessStatusCode();
            return response.Content.ReadAsAsync<Customer>().Result;
        }
    }

Can anyone explain why I'm seeing this behavior and how I can get around it without removing the [Serializable] attribute from my data classes?



Solution 1:[1]

Your best option is to not use the old "serializable" pattern at all. Both Newtonsoft and the modern System.Text.Json serializers will serialize in a sensible way without any attributes.

[JsonObject] from the other answer is also not necessary. In this case all it's really doing is turning on a switch that [Serializable] turned off.

[Serializable] (and associated ISerializable interface) where some of the original ways to define how an object should be treated by things like DataContractSerializer. These are best left in the past :)

It is only being recognized in order to provide some backward compatibility so it behaves approximately the same as the original serializers.

There are plenty of attrs and other hooks to allow for customization. Research into the those provided directly by Newtonsoft or S.T.Json (depending on which you have configured, probably newtonsoft considering framework version you mention).

I would suggest that your future self will thank you for leaving these as pure POCOs (ie completely unattributed, bags of properties).

Solution 2:[2]

Try something like this ...

public static class ApiUtil
{
    static HttpClient GetApiClient()
            {
                var client = new HttpClient();
                client.BaseAddress = new Uri(ConfigurationManager.AppSettings["apiUrl"]);
                return client;
            }

    public static async Task<Customer> GetCustomer(int id)
            {
                var client = GetApiClient();
                string query = "Customers(" + id + ")";
                var response = await client.GetAsync(query);
                return await response.Content.ReadAsAsync<Customer>();
            }
}

then to use this you might do something like this in an async method ...

var customer = await ApiUtil.GetCustomer(1);

if you cant use async and await when calling this try something like ...

var customerTask = ApiUtil.GetCustomer(1);
customerTask.Wait();
var customer = customerTask.Result;

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 AndyPook
Solution 2 War