Make Http Request to Api and Convert to json - c#

My question may be trivial but I have spent almost 6hrs just trying things out.
public async Task<object> save()
{
var uri = "https://newsapi.org/v1/articles?source=talksport&apiKey=longKey";
var httpClient = new HttpClient ();
HttpResponseMessage res = await httpClient.GetAsync(uri);
var data = await res.Content.ReadAsStreamAsync();
// this is what I want to achieve like in python you can do something like this
foreach(var item in data){
Console.writeline(item.summary);
}
// end of arbitrary code
return data;
}
My problem is ,am unable to do this conversion to get the response and then accessing the json data.
In python you can do something
r = request.get(apiUrl)
data = r.json()
for item in data:
print(item.summary)
This is all I have struggle to achieve with c#, Any help to complete the code or explanation. Thanks

Try to use something like this:
Install Newtonsoft.Json package and add using Newtonsoft.Json;
using (var request = new HttpRequestMessage()) {
request.RequestUri = new Uri("https://newsapi.org/v1/articles?source=talksport&apiKey=longKey");
request.Method = HttpMethod.Get;
using (var response = await httpClient.SendAsync(request)) {
string content = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<IList<dynamic>>(content);
foreach(var item in result){
Console.writeline(item.summary);
}
}
}
From comment
Then i get this
"{\"vouchers\":[\"UN9NKK\",\"FYMFVS\",\"WV5AX7\",\"M2TJJ8\",‌​\"FBB9AL\",\"MBW8Z4\‌​"]}"
You can create a new class
public class MyResponse {
public IEnumerable<string> Vouchers {get;set; }
}
then
var response = JsonConvert.DeserializeObject<MyResponse>(content);
foreach(var item in response.Vouchers){
Console.WriteLine(item);
}

If you don't mind a small library dependency, Flurl (disclaimer: I'm the author) gets you Python's simplicity in C#:
var data = await apiUrl.GetJsonAsync();
In this case, data is a C# dynamic type, which means you can access all the JSON object's properties by name without defining a corresponding C# class, much like an untyped language. If you do want to declare a class and get compile-time type checking, that works with Flurl too:
var data = await apiUrl.GetJsonAsync<MyClass>();
Now data is an instance of MyClass instead of a dynamic.
Get Flurl.Http on Nuget, and reference it with using Flurl.Http;.

Related

How do you properly add a C# stand alone script as a step in Octopus Deploy?

So, I'm looking into adding a C# stand alone script as part of a Step in a deployment process, but am having a really hard time finding a reference that show how to properly "format" the script for use by Octopus.
One thing I did find was that all references need to be explicit. So for instance, if the script makes use of HttpClient to make a GET request, you can't rely on a using statements to shorten the reference, but instead have to use the "fully qualified namespace".
So basically instead of being able to do this:
HttpClient client = new HttpClient();
You have to do it like this:
System.Net.Http.HttpClient client = new System.Net.HttpClient()
Okay, so I've modified my script to make explicit reference to any class or method within its given namespace.
Now, what happens if I have a custom class? How do I handle that? I'll illustrate what I mean with an example. Say I have the following:
using System;
namespace MyOctoScript
{
class Person
{
public string name { get; set; }
}
class Script
{
static System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
static System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
public const string endpoint = "some_valid_endpoint";
static async System.Threading.Tasks.Task Main(string [] args)
{
MyOctoScript.Person person = null;
// Use Http Client to fetch JSON from a given endpoint
System.Net.Http.HttpResponseMessage response = await client.GetAsync(endpoint);
// Parse JSON from response
string jsonString = await response.Content.ReadAsStringAsync();
// Store object in variable of type Person
person = serializer.Deserialize<MyOctoScript.Person>(jsonString);
}
}
}
Now, this script works as a console application. I want to ensure it works once I add it as a C# script that is part of a Step.
What changes (if any) do I need to make to the code above to achieve this?
Thanks to anyone in advance!
The Docs say Octopus supports C# via ScriptCS
https://octopus.com/docs/deployment-examples/custom-scripts#supported-script-types
https://github.com/scriptcs/scriptcs
So I'd assume (untested) you'd need to flatten it to something like this:
using System;
class Person
{
public string name { get; set; }
}
static System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
static System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
public const string endpoint = "some_valid_endpoint";
Person person = null;
// Use Http Client to fetch JSON from a given endpoint
System.Net.Http.HttpResponseMessage response = await client.GetAsync(endpoint);
// Parse JSON from response
string jsonString = await response.Content.ReadAsStringAsync();
// Store object in variable of type Person
person = serializer.Deserialize<Person>(jsonString);
tbh, I'm not sure how ScripCS handles Async, so there's some work to do to figure that out.
ScriptCS can be used to run standalone scripts or as a REPL, so you can test it locally.

Correct way to use Async/Await

I've been working for a few days on a performance problem.
Before I delve deeper I want to quickly explain how the specific service work.
I have a main Service that get a request and send requests to other micro-services but the single Entry Point for the user is to Main service, I thinks is more simple to understand with this image:
After the Main service get request from API he do some logic, query the Db and then get a list, every item on the list has Id, to get enrichment about every item the main service create request to one of the micro-service.
For example John request main service, main service get from Db a list of 90 items then the main service will create 90 calls to micro service and return to John single response that include 90 items.
Now the question is only about the right way to create async call to micro service.
This how I develop this part:
GetDetailsAsync(Id, result.Items, request.SystemComponentId);
private static void GetDetailsAsync(string Id, List<MainItem> items, int systemId)
{
var getDetailsTasks = new List<Task>();
foreach (MainItem single in items)
{
getDetailsTasks.Add(SetSingleDetailsAsync(Id, single, systemId));
}
Task.WhenAll(getDetailsTasks);
}
private static async Task SetSingleDetailsAsync(string Id, MainItem single, int systemId)
{
single.ActivityExtendedDetails = await ProcessItemDetailsRequest.GetItemDetailsAsync(Id, single.TypeId,
single.ItemId, systemId);
}
public static Task<JObject> GetItemDetailsAsync(string id, short type,
string itemId, int systemId)
{
var typeList = ActivityTypeDetails.GetActivityTypes();
var url = GetActivityUrl(id, type, itemId, typeList);
if (url == null)
{
throw new Failure($"No url defined for type {type}");
}
try
{
JObject res;
using (var stream = client.GetStreamAsync(url).Result)
using (var sr = new StreamReader(stream))
using (var reader = new JsonTextReader(sr))
{
var serializer = new JsonSerializer();
res = serializer.Deserialize<JObject>(reader);
}
return Task.FromResult(res);
}
catch(Exception ex)
{
Logger.Warn(
$"The uri {url} threw exception {ex.Message}.");
//[Todo]throw exception
return null;
}
}
This code run and the result is not good enough, the CPU rises very quickly and becomes very high, I think that I has a problem on GetItemDetailsAsync func because I use client.GetStreamAsync(url).Result
when using .Result it's block until the task is completed.
So I do some minor change on GetItemDetailsAsync to try to be really async:
public static async Task<JObject> GetItemDetailsAsync(string id, short type,
string itemId, int systemId)
{
var typeList = ActivityTypeDetails.GetActivityTypes();
var url = GetActivityUrl(id, type, itemId, typeList);
if (url == null)
{
throw new Failure($"No url defined for type {type}");
}
try
{
JObject res;
using (var stream = await client.GetStreamAsync(url))
using (var sr = new StreamReader(stream))
using (var reader = new JsonTextReader(sr))
{
var serializer = new JsonSerializer();
res = serializer.Deserialize<JObject>(reader);
}
return res;
}
catch(Exception ex)
{
Logger.Warn(
$"The uri {url} threw exception {ex.Message}.");
//[Todo]throw exception
return null;
}
}
But now I get null where I supposed to get the data that come from Async function.
I try to debugging and I noticed something weird, everything happen likes as I would expect: the methods was called, request to micro-service was executed and get response but the response from the End-Point(which is found on main-service) return before the async method return from micro-service, that cause that I get null instead of my expected data.
I thinks that maybe I don't use correctly async\await and would be happy if anyone could explain how this behavior happens

How to edit array to add keynames, from a httpclient api call in MVC?

I am very new to MVC and making api calls server side and need a little guidance. I have created a simple method to call an api to retrieve results in a JSON object:
apiController.cs (normal controller.cs file)
[HttpGet]
public JsonResult getDefaultStuff(string a = "abc") {
var url = "https://myapiurl";
var client = new HttpClient();
client.DefaultRequestHeaders.UserAgent.ParseAdd("Blah");
var response = client.GetStringAsync(url);
return Json(response, JsonRequestBehavior.AllowGet);
}
The results return in an array like this:
{Result: {examples: [[0000,6.121],[0000,1.122],[0000,9.172]]},"Id":81,"Exception":null,"Status":5,"IsCanceled":false,"IsCompleted":true,"CreationOptions":0,"AsyncState":null,"IsFaulted":false}
I need it to return with keynames like this :
{
"examples": [
{
"Keyname1": "45678",
"Keyname2": "1234"
},
{
"Keyname1": "14789",
"Keyname2": "1234"
},
{
"Keyname1": "12358",
"Keyname2": "4569"
}
]
}
Do I need to use IDictonary? I am unsure of the approach. Do I create a new object and then loop through each result adding keynames? an example would be much appreciated or just the approach will be very helpful.
You can do the following:
By using the Json.Net nuget package first deserialize the response into, for example, an anonymous object:
var deserialized = JsonConvert.DeserializeAnonymousType(response, new
{
examples = new[] { new decimal[] { } }
});
Then transform this object into the new one that has the property structure you need:
var result = new
{
examples = deserialized.Result.examples.Select(x => new
{
Keyname1 = x[0],
Keyname2 = x[1]
})
};
And return it to the client:
return Json(result, JsonRequestBehavior.AllowGet);
This is what your solution could roughly look like but you do have to keep several things in mind though:
Exception handling for deserialization
Checks for possible nulls in the deserialized object
Maybe also the safer way of retrieving values from the array to avoid possible out of range exceptions.
Also the GetStringAsync method is asynchronous and you should put an await keyword in front of it, but in order to do so you need to make your method async as well:
public async Task<JsonResult> getDefaultStuff(...)
If you don't have enough knowledge of asynchronous programming, here is the most advanced, in-depth and comprehensive video explaining it from top to bottom I have ever seen, so check it out whenever you find time...

Google Directory API - How to get the resulting deserialized objects of a BatchRequest

i am trying to speed up some google directory api calls in the .net client library with BatchRequests
lets say i have the following batchRequest (which consists only of one
request for simplicity):
static async Task BatchRequesting()
{
var batchReq = new BatchRequest(_dirservices[0]);
var r = _dirservices[0].Users.Get("user#domain.com");
batchReq.Queue<UsersResource.GetRequest>(r,
(contentReq, error, j, message) =>
{
... what to do here?
});
await batchReq.ExecuteAsync();
}
how do i get the resulting deserialized response object in the callback (which would be a User object in my case)
Do i have to handle the message.Content object (HttpContent) myself with all the json deserializing?
I found the solution. I used the wrong generic parameter. My Code example has to be like this:
static async Task BatchRequesting()
{
var batchReq = new BatchRequest(_directoryService);
var request = _directoryService.Users.Get("user#domain.com");
batchReq.Queue<User>(request,
(returnedUser, error, j, message) =>
{
if (error != null)
{
Console.WriteLine(error.Message);
}
else
{
... work with returnedUser
}
});
await batchReq.ExecuteAsync();
}

Json.NET - deserialize directly from a stream to a dynamic?

With a little help from the performance tips in the Json.NET docs, I put together a method for downloading/deserializing JSON from a remote resource:
public async Task<T> GetJsonAsync<T>(string url)
{
using (var stream = await new HttpClient().GetStreamAsync(url))
{
using (var sr = new StreamReader(stream))
{
using (var jr = new JsonTextReader(sr))
{
return new JsonSerializer().Deserialize<T>(jr);
}
}
}
}
I'd like to have a non-generic version that returns a dynamic. Calling the above method with GetJsonAsync<dynamic>(url) works, until you try to access a dynamic property on the result, at which point I get:
'Newtonsoft.Json.Linq.JObject' does not contain a definition for '[MyProperty]'
I have seen how to deserialize to a dynamic from a string, but have not seen a working example of doing it directly from a stream, which would be preferable as it is more memory efficient. Is this possible?
It turns out this had little to do with Json.NET and more to do with my understanding of dynamics (which I rarely use). Thanks to #Peter Richie, I found that GetJsonAsync<dynamic> does work if I explicitly cast MyProperty to a string. But I'd rather not have to do that. Using my original method and a real working endpoint, here are 3 scenarios; only the last one works:
var url = "http://echo.jsontest.com/MyProperty/MyValue"; // great testing site!
var x1 = await GetJsonAsync<dynamic>(url);
Assert.AreEqual("MyValue", x1.MyProperty); // fail!
dynamic x2 = await GetJsonAsync<dynamic>(url);
Assert.AreEqual("MyValue", x2.MyProperty); // fail!
dynamic x3 = await GetJsonAsync<ExpandoObject>(url);
Assert.AreEqual("MyValue", x3.MyProperty); // pass!
Armed with that knowledge, the non-generic overload of my original method looks like this:
public async Task<dynamic> GetJsonAsync(string url) {
dynamic d = await GetJsonAsync<ExpandoObject>(url);
return d;
}
And users can do this:
var x = await GetJsonAsync(url);
Assert.AreEqual("MyValue", x.MyProperty); // pass!
It sounds like there's some information you haven't provided. The following works fine for me:
private T ReadJson<T>(Stream stream)
{
using (var reader = new StreamReader(stream))
{
using (var jr = new JsonTextReader(reader))
{
dynamic d = new JsonSerializer().Deserialize(jr);
return d;
}
}
}
//...
var d = ReadJson<dynamic>(new MemoryStream(Encoding.UTF8.GetBytes("{'MyProperty' : 'MyValue'}")));
Debug.WriteLine((String)d.MyProperty);

Categories