Home > Software design >  JSON string will not deserialize into the type specified
JSON string will not deserialize into the type specified

Time:11-28

I have the following bit of code whihc sends a Http POST request to the server. The server reurns a 400 Bad request response along with a error object in the form of Json:

namespace MyApp.Shared.Dtos.Response
{
    public class ErrorItem
    {
        public string Message { get; set; }
        public string Tag { get; set; }
    }

    public class ErrorDto
    {
        public string Title { get; set; }
        public List<ErrorItem> Errors { get; set; } = new();
    }
}

namespace Accounting.Web.Services
{
    public interface IHttpService
    {
        Task<T> Get<T>(string uri);
        Task<T> Post<T>(string uri, object value, bool addBearerToken = false);
        public ErrorDto Error { get; set; }
    }

    public class HttpService: IHttpService
    {
        private HttpClient _httpClient;
        public ErrorDto Error { get; set; }

        public HttpService(HttpClient httpClient)
        {
            _httpClient = httpClient;
            _stateService = stateService;
        }

        public async Task<T> Post<T>(string uri, object value)
        {
            var request = new HttpRequestMessage(HttpMethod.Post, uri);
            request.Content = new StringContent(JsonSerializer.Serialize(value), Encoding.UTF8, "application/json");
            return await sendRequest<T>(request, addBearerToken);
        }

        private async Task<T> sendRequest<T>(HttpRequestMessage request)
        {
            using var response = await _httpClient.SendAsync(request);
            
            if (response.StatusCode == System.Net.HttpStatusCode.BadRequest)
            {
                var result = await response.Content.ReadAsStringAsync();
                Error = JsonSerializer.Deserialize<ErrorDto>(result);
                //..
            } 
            else
            {
                //..
            }            
        }
    }
}

The result correctly recieves the following response from the server as a JSON string:

{"title":"Username or password is incorrect","errors":[]}

And I can confirm by inspecting var result, it has the above value.

However, It doesn't seem deserialize into the ErrorDto class as one would expect it to: Error = JsonSerializer.Deserialize(result);

But I simply cannot see any problems with the code, it looks like it should be working.

*** UPDATE ***

My server API code returrns the JSOn using the same DTO class (It's a shared class) using the following code:

[HttpPost("authenticate")]
public ActionResult Authenticate(AuthenticateRequest loginRequest)
{
    var auth = _userService.Authenticate(loginRequest);
    ErrorDto error = new()
    {
        Title = "Username or password is incorrect"
    };
    if (auth.user == null || auth.token == null)
    {
        return BadRequest(error);
    }
    return Ok(auth.user.ConvertToDto(auth.token));
}

CodePudding user response:

By default System.Text.Json is case-sensitive. There are multiple options to handle this, for example by providing corresponding JsonSerializerOptions:

var json = @"{""title"":""Username or password is incorrect"",""errors"":[]}";
var errorDto = JsonSerializer.Deserialize<ErrorDto>(json, new JsonSerializerOptions
{
    PropertyNameCaseInsensitive = true
});

Or marking properties with corresponding JsonPropertyNameAttribute:

public class ErrorItem
{
    [JsonPropertyName("message")]
    public string Message { get; set; }
    [JsonPropertyName("tag")]
    public string Tag { get; set; }
}

public class ErrorDto
{
    [JsonPropertyName("title")]
    public string Title { get; set; }

    [JsonPropertyName("errors")]
    public List<ErrorItem> Errors { get; set; } = new();
}

UPD

From How to customize property names and values with System.Text.Json doc:

Note
The web default is camel case.

If you want to switch from camel case to the naming policy used for DTOs you can do the following:

builder.Services.AddControllers()
    .AddJsonOptions(opts => opts.JsonSerializerOptions.PropertyNamingPolicy = null);
  • Related