I'm building a class library that connects to a client's API, and collects the response from a given endpoint into a class. In other words, I'll hit the client's Donuts endpoint, and using JsonConvert to deserialize the response into my Donuts class, which has fields that match the attributes in the JSON object. This is all well and good, but they have a lot of endpoints, and I have many methods which repeat the same code over and over. In the interests of DRYness and separation of concerns, I'd like to have a separate namespace that makes the rest call, no matter the endpoint, and returns the correct result to the main class. Here's an example of what it does now (I'm using RestSharp to configure the request and response):
public List<T> Index<T>(string Endpoint)
{
List<T> Results = new List<T>();
RestClient Client = new RestClient();
RestRequest Req = ConfigureGetRequest(Endpoint);
IRestResponse Resp = Client.Execute(Req);
if (Resp.StatusCode == System.Net.HttpStatusCode.OK)
{
Results = JsonConvert.DeserializeObject<List<T>>(Resp.Content, DeserializationSettings);
}
return Results;
}
}
There's other code in the if statement (it's actually an if-else for error handling), but it's not really relevant to this question. I have this same code in multiple methods, with the only difference being that it returns List<Bagel>, or List<Cruller>, etc... What I'd like to do is extract this code into it's own method, but I'm not sure how. Right now, I'm trying to deserialize into List<object>, and then convert it to List<Donut> later, but that doesn't seem to be something I can do. My other thought is to pass in the endpoint name as a parameter (public List<Donut> GetDonuts(string Endpoint)), and then somehow use that to determine what type of object to deserialize into, if that makes sense? Anyhoo, if anyone has any insight on a good way to do this, it would be appreciated!
EDIT
Awesome responses, thank you folks. I've refactored the method according to Matt Dillard's answer below. The next step is to convert the List to List. The method that calls the method above looks like this:
public List<Donut> GetDonuts()
{
List<Donut> Results = new List<Donut>();
MODULE MakeCall = new GET();
return Results = MakeCall.Index("donuts")();
}
My Intellisense tells me that "The type arguments for method 'MODULE.Index(string)' cannot be inferred from the usage. Try specifying the type arguments explicitly". I tried casting like so: (List)MakeCall.Index("donuts")();
but that didn't work out. Is there a way to cast this so that I can have methods GetDonuts, GetCrullers, etc... that all call Index effectively?
Generics:
public List<T> GetStuff<T>(string endpoint)
{
List<T> Results = new List<T>();
RestClient Client = new RestClient();
RestRequest Req = ConfigureGetRequest(endpoint);
IRestResponse Resp = Client.Execute(Req);
if (Resp.StatusCode == System.Net.HttpStatusCode.OK)
{
Results = JsonConvert.DeserializeObject<List<T>>(Resp.Content, DeserializationSettings);
}
return Results;
}
}
Bascially replace all references to Donut with T.
Presumably the endpoint address would change, so you can add that as a parameter.
Call something like this:
var donuts = GetStuff<Donut>("http://dunkin.com/api/allthedonuts");
You probably would like to have it as generic method which accepts a type T and return a list of such like
public List<T> GetDonuts<T>() where T : class, new()
{
I figured out what needed to happen. I can't deserialize a generic list, but I can deserialize a generic, and place it in a list. The final code looks like this:
public List<Donut> GetDonuts<T>()
{
GET MakeCall = new GET();
List<Donut> Results = new List<Donut>();
Results = MakeCall.Index<Donut>();
return Results;
}
public List<T> Index<T>()
{
T Result;
List<T> ResultList = new List<T>();
RestClient Client = new RestClient();
RestRequest Req = ConfigureGetRequest(Endpoint);
IRestResponse Resp = Client.Execute(Req);
if (Resp.StatusCode == System.Net.HttpStatusCode.OK)
{
Result = JsonConvert.DeserializeObject<T>(Resp.Content, DeserializationSettings);
ResultList.Add(Result);
}
return ResultList;
}
This allows me to use GetDonuts, GetCrullers, etc... to be lean and DRY. Thanks for all the help!
Related
I am calling a api and getting list of ids in json format in restsharp api response. But problem i am unable to write the syntax for List in restsharp Execute method. Please check the code bellow. And correct my List<> Syntax.
var client2 = new RestClient(apiEndPoint);
var request2 = new RestRequest(Method.GET);
var Result2 = client.Execute<List><PageIds>(request);
My id model:
class PageIds
{
public int Id { get; set; }
}
Hay i think this is your answer-
var Result2 = client.Execute<List<PageIds>>(request);
However until we see your json response cant sure this will work or not. But at-least it is the correct syntax for List on this particular situation.
I'm trying to deserialise a response from a REST service into C# Strongly typed classes - however I've ran into the same issue has in this post:
How do I output this JSON value where the key starts with a number?
However I have the issue that you cannot start a variable name in c# with a number - meaning that the class at that level just deserialises into null.
I need to know how to get into the objects and deserialise them into the c# classes.
My Current code is below:
public static async Task<T> MakeAPIGetRequest<T>(string uri)
{
Uri requestURI = new Uri(uri);
using (HttpClient client = new HttpClient())
{
HttpResponseMessage responseGet = await client.GetAsync(requestURI);
if (responseGet.StatusCode != HttpStatusCode.OK)
{
throw new Exception(String.Format(
"Server error (HTTP {0}: {1}).",
responseGet.StatusCode,
responseGet.Content));
}
else
{
string response = await responseGet.Content.ReadAsStringAsync();
T objects = (JsonConvert.DeserializeObject<T>(response));
return objects;
}
}
}
EDIT: I cannot change the way the service is pushing the data back
The correct way to deal with this was to use the JsonProperty tag on the target classes to define what Json property to listen for, as shown below (referenced from https://stackoverflow.com/questions/24218536/deserialize-json-that-has-some-property-name-starting-with-a-number
public class MyClass
{
[JsonProperty(PropertyName = "24hhigh")]
public string Highest { get; set; }
...
Thanks to #HebeleHododo for the comment answer
While there is no direct way to construct a strongly typed C# object in this case, You could still have the capabilities to parse the json string manually and extract values -
var json = "{'1':{'name':'test','age':'test'}}";
var t = JObject.Parse(json)["1"];
Console.WriteLine(t["name"]); //test
Console.WriteLine(t["age"]); //test
I am making of System.Net.Http.HttpClient class to call an end point. This endpoint is expect certain input and returns a List of user defined object of type Employee ( List<Employee>).
This is the code that I am using.
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:7792/");
client.DefaultRequestHeaders.Accept.Clear();
FilterModel payload = new FilterModel();
payload.employeeId= 97050001;
payload.Level= "Manager";
// New code:
HttpResponseMessage response = client.PostAsJsonAsync("api/employee", payload).Result;
if (response.IsSuccessStatusCode)
{
var employee= response.Content.ReadAsStringAsync();
//HOW DO I CONVERT THE OUTPUT INTO LIST<EMPLOYEE>?
Console.Write("---DONE---");
}
Console.ReadKey();
}
I know this is not the ideal way to call an end point & I must use asyc await. I just need the data, the call can be sync or async & I want to type cast the result into List.
Currently I get a string back which I need to deserialize. Please help
You can use JsonConvert as below:
var jsonString= response.Content.ReadAsStringAsync();
var employees = JsonConvert.DeserializeObject<List<Employee>>(jsonString);
Hope this help!
UPDATE 2020:
Use some thing like this:
return await response.Content.ReadFromJsonAsync<List<Employee>>();
I have started using RestSharp to call an webapi proejct as it seems pretty easy to use.
I am wanting to build a helper class for all of my crud actions.
I have this so far for a simple PUT request.
public static IRestResponse Update(object objectToUpdate,string apiEndPoint)
{
var client = new RestClient(CreateBaseUrl(null))
{
Authenticator = new HttpBasicAuthenticator("user", "Password1")
};
var request = new RestRequest(apiEndPoint, Method.PUT);
request.AddObject(objectToUpdate);
var response = client.Execute<MyViewModel>(request);
//var response = client.ExecuteDynamic(request);
return response;
}
So the above code works however I have had to hardcode my viewmodel into it
var response = client.Execute<MyViewModel>(request);
How can I change this so I dont need to know the type of model I am expecting?
I tried using var response = client.ExecuteDynamic(request);
however this throws an exception of
Unable to cast object of type 'RestSharp.RestResponse' to type 'RestSharp.RestResponse`1[System.Object
Im not sure how I am meant to cast my object correctly
I'm not familiar with RestSharp. However, it sounds like generics could help you here. Either your class or method needs to accept a type. For example, the signature of your method would change to
public static IRestResponse Update<T>(object objectToUpdate,string apiEndPoint)
This would allow you to call the method as:
Update<MyViewModel>(objectToUpdate, apiEndPoint);
Your implementation would change from your concrete type to:
var response = client.Execute<T>(request);
Overall you could modify your code to something like this:
public static IRestResponse Update<T>(object objectToUpdate,string apiEndPoint)
{
var client = new RestClient(CreateBaseUrl(null))
{
Authenticator = new HttpBasicAuthenticator("user", "Password1")
};
var request = new RestRequest(apiEndPoint, Method.PUT);
request.AddObject(objectToUpdate);
var response = client.Execute<T>(request);
//var response = client.ExecuteDynamic(request);
return response;
}
Documentation on C# Generics can be found here: http://msdn.microsoft.com/en-us/library/ms379564(v=vs.80).aspx
I have the following code:
public void GetJson()
{
RestRequest request = new RestRequest(Method.GET);
var data = Execute<Dictionary<string, MyObject>>(request);
}
public T Execute<T>(RestRequest request) where T : new()
{
RestClient client = new RestClient(baseUrl);
client.AddHandler("text/plain", new JsonDeserializer());
var response = client.Execute<T>(request);
return response.Data;
}
The problem is that sometimes the response will be an empty json array []. And when I run this code I get the following exception: Unable to cast object of type 'RestSharp.JsonArray' to type 'System.Collections.Generic.IDictionary`2[System.String,System.Object]'.
Is there a way to gracefully handle this?
I worked around a similar issue myself in the following way. I had tried using custom deserializers (since I was deserializing to a complex object) but in the end the following was much simpler, as it only applied to one of the many kinds of requests I was making.
request.OnBeforeDeserialization = (x =>
{
x.Content = x.Content.Replace("[]", "{}");
});
Where I was constructing the request object for this particular request, I used the OnBeforeDeserialization property to set a callback which replaces the incorrect [] with {}. This works for me because I know the data I'm getting back in the rest of x.Content will never contain [] except in this specialized case, even in the values.
This might help someone else, but should definitely be used with care.
I've never needed the client.AddHandler line, so I'm not sure you need that. Try this for your Execute method, though:
public T Execute<T>(RestRequest request) where T : class, new()
{
RestClient client = new RestClient(baseUrl);
client.AddHandler("text/plain", new JsonDeserializer());
try
{
var response = client.Execute<T>(request);
return response.Data;
}
catch (Exception ex)
{
// This is ugly, but if you don't want to bury any errors except when trying to deserialize an empty array to a dictionary...
if (ex is InvalidCastException && typeof(T) == typeof(Dictionary<string, MyObject>))
{
// Log the exception?
return new T();
}
throw;
}
}