I'm trying to implement a generic HTTP service that performs requests to my OData API based on the type of object that gets passed onto the method. For example: if I pass an object of type Employee, it will retrieve all employees, and if I pass an object of type Customer, it will retrieve all customers.
This is an example API response that returns a list of all employees:
{
"@odata.context": "https://localhost:44375/odata/$metadata#Employees",
"@odata.count": 3,
"value": [
{
"EmployeeType": "Manager",
"FirstName": "Test",
"LastName": "Test",
"Address": "Test",
"PostalCode": "1234AB",
"City": "Test",
"PhoneNumber": "0641615077",
"EmailAddress": "[email protected]",
"HostelId": 1,
"Id": 1,
"DateCreated": "0001-01-01T00:00:00Z",
"LastModifiedDate": "0001-01-01T00:00:00Z"
},
...
]
}
I want to deserialize both the count and values into an object called EntityListResponse.
public class EntityListResponse
{
[System.Text.Json.Serialization.JsonPropertyName("value")]
public List<object> Records { get; set; }
[System.Text.Json.Serialization.JsonPropertyName("@odata.count")]
public int Total { get; set; }
}
I have built my own Blazor component which receives an object of any type as a parameter:
[Parameter]
public TItem Entity { get; set; }
The component has a method called "ReadEntity" which calls my generic HTTP service. The first parameter is needed for OData requests, the second parameter is the object - Employee for example.
private async Task ReadEntity(GridReadEventArgs args)
{
var data = await _entityService.GetAll(args.Request, Entity);
...
}
The GetAll method in the HTTP service:
public async Task<EntityListResponse> GetAll(DataSourceRequest request, object entity)
{
var baseUrl = $"{entity}s?";
var requestUrl = $"{baseUrl}{request.ToODataString()}";
var requestMessage = new HttpRequestMessage(HttpMethod.Get, requestUrl);
requestMessage.Headers.Add("Accept", "application/json");
var client = HttpClient.CreateClient("IndiciaStageApi");
var response = await client.SendAsync(requestMessage);
if (response.IsSuccessStatusCode)
{
var body = await response.Content.ReadAsStringAsync();
var options = new JsonSerializerOptions();
options.Converters.Add(new JsonStringEnumConverter());
var oDataResponse = JsonSerializer.Deserialize<EntityListResponse>(body, options);
return oDataResponse;
}
else
{
throw new HttpRequestException(
"Request failed. I need better error handling, e.g. returning empty data.");
}
}
Everything in the method works perfectly except for the deserialization. The problem is that I have no idea how to let the deserializer know of which type the Records property of EntityListResponse should be. Now it just creates a list of JSON objects:
I would like the object type of the list to be determined based on the object type that gets passed on. So if I pass an object of type Employee, it should deserialize the response body into List, if I pass an object of type Customer it should deserialize into List, and so on.
Is this even possible to begin with?
CodePudding user response:
You can use this but make your method accept generic type parameter instead (https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/types/generics).
So your method could be something like:
async Task<T> GET<T>(string endPoint).
Then when you what to Deserialize, use the T there.
var oDataResponse = JsonSerializer.Deserialize<T>(body, options);
CodePudding user response:
Use a Generic class:
public class OdataResponce<T>
{
[JsonPropertyName(name: "@odata.context")]
public string ContextUrl { get; set; }
[JsonPropertyName(name: "@odata.count")]
public int Count { get; set; }
[JsonPropertyName(name: "value")]
public IEnumerable<T> Data { get; set; }
}
Usage:
var odataResponce =
await http.GetFromJsonAsync<OdataResponce<WeatherForecast>>(query);