I have a function that calls API requests, and I want to have each request return its own data type according to its purpose.
For that, I created a class called ApiResponse
. Here is its code:
public class ApiResponse<T>
{
public bool Success { get; set; }
public string? ErrorMessage { get; set; }
public T? Data { get; set; }
public ApiResponse()
{
Success = true; // Default to success
}
}
The way I use this class is when I request DB queries, the data, which is of type T
will be DBResponse
. Here is its implementation:
public class DBResponse<T>
{
public int Code { get; set; }
public string Message { get; set; }
public bool Success { get; set; }
public T? Data { get; set; }
public DBResponse(int code, string message, bool success, T? data = default)
{
Code = code;
Message = message;
Success = success;
Data = data;
}
}
And, the method I created for calling the API requests is:
public async Task<ApiResponse<T>> SendRequestAsync<T>(string route, object? body = null)
{
var appSettings = new AppSettings();
try
{
// Prepare the request payload
var requestPayload = new
{
Database = "my-database",
Username = "my-user",
Password = "my-password",
Body = body
};
var content = new StringContent(JsonSerializer.Serialize(requestPayload), Encoding.UTF8, "application/json");
// Call the API
var response = await httpClient.PostAsync($"{appSettings.ApplicationUrl}/request/{route}", content);
if (response.IsSuccessStatusCode)
{
// Get the raw response JSON as a string
var rawResponseJson = await response.Content.ReadAsStringAsync();
try
{
// Deserialize the raw response JSON into ApiResponse<T>
var apiResponse = JsonSerializer.Deserialize<ApiResponse<T>>(rawResponseJson, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
if (apiResponse == null)
{
return new ApiResponse<T>
{
Success = false,
ErrorMessage = "No data returned from the API."
};
}
return apiResponse;
}
catch (Exception ex)
{
return new ApiResponse<T>
{
Success = false,
ErrorMessage = $"Error deserializing response to type {typeof(T).Name}: {ex.Message}"
};
}
}
else
{
return new ApiResponse<T>
{
Success = false,
ErrorMessage = $"HTTP Error: {response.StatusCode} - {response.ReasonPhrase}"
};
}
}
catch (Exception ex)
{
return new ApiResponse<T>
{
Success = false,
ErrorMessage = $"SendRequestAsync general error: {ex.Message}"
};
}
}
I have one API request that fetches records from the database, hence its return type is ApiResponse<DBResponse<List<Dictionary<string, object>>>>
. In this case, the function works well.
However, when I am doing an update, insert or delete SQL commands, the return type of the call is: ApiResponse<DBResponse<int>>
.
In this case, I am getting an error on the command:
var apiResponse = JsonSerializer.Deserialize<ApiResponse<T>>(rawResponseJson, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
Can someone please help me understand the problem I am having with the Deserialize
call? Why can’t it convert simpler types from JSON?
I tried calling the Deserialize call without the PropertyNameCaseInsensitive = true
, but that is worse.
Here is an example for JSON that works well:
{"success":true,"errorMessage":"","data":{"code":0,"message":"","success":true,"data":[{"Id":5,"Name":"One Value Updated Value Updated Value","CreatedOn":"2025-01-02T04:44:40"}]}}
And, here is an example for JSON that fails:
{"success":true,"errorMessage":"","data":{"code":0,"message":"Update successful.","success":true,"data":1}}
With this error message:
System.Text.Json.JsonException
HResult=0x80131500
Message=The JSON value could not be converted to Project.SharedLibrary.DBResponse`1[System.Int32]. Path: $.data.data | LineNumber: 0 | BytePositionInLine: 105.