I need to write the titles of the countries from VK API to a list from the following link.
I have written some code:
public class GettingCountry
{
public async Task<string> FetchAsync(string url)
{
string jsonString;
using (var httpClient = new System.Net.Http.HttpClient())
{
var stream = await httpClient.GetStreamAsync(url);
StreamReader reader = new StreamReader(stream);
jsonString = reader.ReadToEnd();
}
var readJson = JObject.Parse(jsonString);
string countryName = readJson["response"]["items"].ToString();
var deserialized = JsonConvert.DeserializeObject<RootObject>(jsonString);
return jsonString;
}
}
public class Item
{
public int id { get; set; }
public string title { get; set; }
}
public class Response
{
public int count { get; set; }
public List<Item> items { get; set; }
}
public class RootObject
{
public Response response { get; set; }
}
}
I put a breakpoint and got in string countryName only this:
As you can see the VK API returns you an array of objects.
Your jsonString contains full response string. Now, jsonString["response"]["items"] contains an array of items.
First you need to parse the array and then parse each item, like this:
var readJson = JObject.Parse(jsonString);
JArray countries = JArray.Parse(readJson["response"]["items"]);
var Response listOfCountries = new Response();
foreach (var country in countries) {
Item currentCountry = new Item();
currentCountry.id = country.id;
currentCountry.title = country.title;
listOfCountries.items.Add(currentCountry);
}
listOfCountries.count = listOfCountries.items.Count;
From the code perspective, I would recommend giving proper names to variables, classes, and types to improve code readability and cleanliness. On top of it, I don't really see a point in having a separate Response class. For example, you could rename your Item class and call it Country. All you need to have is a list of countries then. Also, because you're using async method, you want to do the processing of the return within your using for the HttpClient - if you don't the client might be disposed too soon and you might start hitting very weird bugs. Like this:
public class VkCountry
{
public int Id { get; }
public string Title { get; }
public VkCountry(int countryId, string countryTitle) {
this.Id = countryId;
this.Title = countryTitle;
}
}
public async Task<List<VkCountry>> FetchAsync(string url)
{
string jsonString;
using (var httpClient = new System.Net.Http.HttpClient())
{
var stream = await httpClient.GetStreamAsync(url);
StreamReader reader = new StreamReader(stream);
jsonString = reader.ReadToEnd();
var listOfCountries = new List<VkCountry>();
var responseCountries = JArray.Parse(JObject.Parse(jsonString)["response"]["items"].ToString());
foreach (var countryInResponse in responseCountries) {
var vkCountry = new VkCountry((int)countryInResponse["id"], (string)countryInResponse["title"]);
listOfCountries.Add(vkCountry);
}
return listOfCountries;
}
}
You might notice that I've made VkContry implementation immutable, the properties are read-only and can be set using the constructor only. I'd recommend using immutable objects when you are consuming relatively static third-party APIs (list of countries is definitely static, unless some kind of application logic will require you to update the name of the country).
Obviously, you might want to add nullability checks and different validations.
Related
So I am using TDAmeritrade API to receive stock data with a C# Winforms program on Visual Studio. It takes the user input stock symbol and searches for the info. I am using HttpClient and Newtonsoft.Json and have been able to successfully perform the GET request and receive a JSON string back, but I do not know how to get all of the information I need out of it.
Here is the JSON:
https://drive.google.com/file/d/1TpAUwjyqrHArEXGXMof_K1eQe0hFoaw5/view?usp=sharing
Above is the JSON string sent back to me then formatted. My goal is to record information for each price in "callExpDateMap.2021-02-19:11" and "callExpDateMap.2021-03-19:39". The problem is that for each different stock, the dates that show up in "callExpDateMap" are going to be different.
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await client.GetAsync(url);
var info = await response.Content.ReadAsStringAsync();
dynamic config = JsonConvert.DeserializeObject<dynamic>(info, new ExpandoObjectConverter());
return config;
This is the code I have right now. I know the last for statement is not correct. How can I parse to the specific sections I want (callExpDateMap.expirationdate.StrikePrice) and get the information needed from each without knowing the dates and Strike prices beforehand? Is there a way to innumerate it and search through the JSON as if it were all a bunch of arrays?
The code below is perhaps not the most elegant nor complete, but I think it will get you going. I would start by using the JObject.Parse() from the Newtonsoft.Json.Linq namespace and take it from there.
JObject root = JObject.Parse(info);
string symbol = root["symbol"].ToObject<string>();
foreach (JToken toplevel in root["callExpDateMap"].Children())
{
foreach (JToken nextlevel in toplevel.Children())
{
foreach (JToken bottomlevel in nextlevel.Children())
{
foreach (JToken jToken in bottomlevel.Children())
{
JArray jArray = jToken as JArray;
foreach (var arrayElement in jArray)
{
InfoObject infoObject = arrayElement.ToObject<InfoObject>();
Console.WriteLine(infoObject.putCall);
Console.WriteLine(infoObject.exchangeName);
Console.WriteLine(infoObject.multiplier);
}
}
}
}
}
public class InfoObject
{
public string putCall { get; set; }
public string symbol { get; set; }
public string description { get; set; }
public string exchangeName { get; set; }
// ...
public int multiplier { get; set; }
// ...
}
This is official documentation of Newtonsoft method you are trying to use.
https://www.newtonsoft.com/json/help/html/Overload_Newtonsoft_Json_JsonConvert_DeserializeObject.htm
If an API's method returns different json propeties and you cannot trust it's property names all the times, then you can try using a deserialize method that returns .Net object, for example: JsonConvert.DeserializeObject Method (String)
https://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_JsonConvert_DeserializeObject.htm
That method's signature is this:
public static Object DeserializeObject(string value)
Parameter is: value of type json string.
Return Value is: Object of type object.
If you do not want an Object, then you can of course use a .Net type you have. Such as this method:
JsonConvert.DeserializeObject Method (String)
Any property that you have in both (the .net type and json object) will get populated. If .net type has properties that do not exist in json object, then those will be ignored. If json object has properties that do not exist in.net, then those will be ignored too.
Here's an example of a .Net type
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace MyNameSpace
{
public class TDAmeritradeStockData
{
[JsonProperty("symbol")]
public string Symbol { get; set; }
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("callExpDateMap")]
public object CallExpDateMap { get; set; }
//...
//...
public CallExpDateMapType[] CallExpDateMapList { get; set; }
}
public class CallExpDateMapType
{
[JsonProperty("expirationdate")]
public string Expirationdate { get; set; }
[JsonProperty("StrikePrice")]
public List<StrikePriceType> StrikePriceList { get; set; }
}
public class StrikePriceType
{
public string StrikePrice { get; set; }
public List<StrikePricePropertiesType> StrikePricePropertiesList { get; set; }
}
public class StrikePricePropertiesType
{
[JsonProperty("putCall")]
public string PutCall { get; set; }
[JsonProperty("symbol")]
public string Symbol { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("exchangeName")]
public string ExchangeName { get; set; }
[JsonProperty("bid")]
public double Bid { get; set; }
[JsonProperty("ask")]
public double Ask { get; set; }
//...
//...
}
[TestClass]
public class TestTestTest
{
[TestMethod]
public void JsonTest()
{
var jsondata = ReadFile("data.json");
var model = JsonConvert.DeserializeObject<TDAmeritradeStockData>(jsondata);
JObject jObject = (JObject)model.CallExpDateMap;
var count = ((JObject)model.CallExpDateMap).Count;
model.CallExpDateMapList = new CallExpDateMapType[count];
var jToken = (JToken)jObject.First;
for (var i = 0; i < count; i++)
{
model.CallExpDateMapList[i] = new CallExpDateMapType
{
Expirationdate = jToken.Path,
StrikePriceList = new List<StrikePriceType>()
};
var nextStrikePrice = jToken.First.First;
while (nextStrikePrice != null)
{
var nextStrikePriceProperties = nextStrikePrice;
var srikePriceList = new StrikePriceType
{
StrikePrice = nextStrikePriceProperties.Path,
StrikePricePropertiesList = JsonConvert.DeserializeObject<List<StrikePricePropertiesType>>(nextStrikePrice.First.ToString())
};
model.CallExpDateMapList[i].StrikePriceList.Add(srikePriceList);
nextStrikePrice = nextStrikePrice.Next;
}
jToken = jToken.Next;
}
Assert.IsNotNull(model);
}
private string ReadFile(string fileName)
{
using (var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
var data = new StringBuilder();
using (var streamReader = new StreamReader(fileStream))
{
while (!streamReader.EndOfStream) data.Append(streamReader.ReadLine());
streamReader.Close();
}
fileStream.Close();
return data.ToString();
}
}
}
}
I have a .net core console application where I am trying to get the values of two properties that are within a returned JSON object. I have highlighted where the error is on line 43, can someone please show me where I am going wrong thank you!
Error:
System.ArgumentException: Accessed JArray values with invalid key value: "name". Int32 array index expected.
at Newtonsoft.Json.Linq.JArray.get_Item(Object key)
at PBXApp.Program.GetUser() in C:\Development\PBXApp\PBXApp\Program.cs:line 43
Returned JSON:
{
"results":[
{
"name":"bulbasaur",
"url":"https://pokeapi.co/api/v2/pokemon/1/"
},
{
"name":"ivysaur",
"url":"https://pokeapi.co/api/v2/pokemon/2/"
},
{
"name":"venusaur",
"url":"https://pokeapi.co/api/v2/pokemon/3/"
}
]
}
Program.cs -
// retrieve asynchronous data from remote api
public static async Task GetUser()
{
//baseUrl
string baseUrl = "http://pokeapi.co/api/v2/pokemon/";
try
{
// HttpClient implements a IDisposable interface
using (HttpClient client = new HttpClient())
{
//initiate Get Request (await will execute the using statement in order)
using (HttpResponseMessage res = await client.GetAsync(baseUrl))
{
//get content from response, then convert it to a c# object
using (HttpContent content = res.Content)
{
//assign content to data variable by converting into a string using await
var data = await content.ReadAsStringAsync();
//convert data using newtonsoft JObject Parse class method
if (data != null)
{
//parse data into an object.
var dataObj = JObject.Parse(data)["results"];
enter code here
//ERROR ON THIS LINE
UserItem userItem = new UserItem(name: $"{dataObj["name"]}", url: $"{dataObj["url"]}");
//log userItem name and url to console
Console.WriteLine("Name:{0} Url:{1}", userItem.Name, userItem.Url);
}
else
{
Console.WriteLine("No returned data");
}
}
}
}
}
//exceptions
catch (Exception exception)
{
Console.WriteLine(exception);
}
}
UserItem.cs -
public class UserItem
{
public UserItem(string name, string url)
{
Name = name;
Url = url;
}
public string Name { get; set; }
public string Url { get; set; }
}
dataObj is a JSON array, but not with named keys. It is just a regular array. So you can't access it with a key named "name", because that key doesn't exist.
You could use a numeric index, but you don't know in advance (from the JObject) how long the array is. So it's better to parse it as an array, and loop over the items:
var dataObj = JArray.Parse(data)["results"];
foreach (var record in dataObj)
{
var name = record["name"];
// ...
}
But why go through all the hassle of manually parsing JSON? Just create classes to hold the results, and deserialize all at once:
public class Pokémon
{
public string name { get; set; }
public string url { get; set; }
}
public class ListOfPokémon
{
public List<Pokémon> results { get; set; }
}
var result = JsonConvert.DeserializeObject<ListOfPokémon>(jsonString);
foreach (var p in result.results)
{
// ...
}
I am doing a bot for VK on C# and faced to some problems. I have method which returns JSON like this
{
"ts": 1674111105,
"updates": [[4,
2262,
17,
61835649,
1534493714,
"",
{
"attach1_type": "doc",
"attach1": "61835649_472186415",
"title": " ... "
}
]]
}
This is object, as I see, but I cant get anything from the attach_type1 to title including. This is also an object, and it can't be transformed to string just like .ToString(), because in that case in the result I have System.Object. So, does anybody know how I can change this type or is it impossible?? I am in desperation.
I created a class for this object
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.Serialization;
namespace CSharpVKbot.VK.UserLongPoll
{
[DataContract()]
public class Attachment
{
[DataMember(Name = "attach1_type")]
public string AttachType;
[DataMember(Name = "attach1")]
public string Attach;
[DataMember(Name = "title")]
public string Title;
}
}
created an object of this class
public Attachment DocId = new Attachment();
and then tried to change type to attachment, but it doesnt work either
case UpdateCode.NewMessage:
u.MessageID = (int)item[1];
u.Flags = (int)item[2];
u.PeerID = (int)item[3];
u.TimeStamp = (int)item[4];
u.Text = (string)item[5];
u.DocId = (Attachment)item[6];
break;
You need to deserialise the JSON - It cannot be just converted to an object.
Try something like
Attachment deserializedAttachement = new Attachment();
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(item[6]));
DataContractJsonSerializer ser = new DataContractJsonSerializer(deserializedAttachment.GetType());
deserializedAttachment = ser.ReadObject(ms) as Attachment;
ms.Close();
Where item[6] is the string that represents the attachment information.
See - https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-serialize-and-deserialize-json-data#example
I think that you will have to iterate through all json properties.
This code may help you
dynamic obj = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(YourJsonString);
foreach (var prop in obj)
{
if (prop is Newtonsoft.Json.Linq.JObject)
{
// Handle JObject
}
if (prop is Newtonsoft.Json.Linq.JProperty)
{
// Handle JProperty
}
}
There are many direction to resolve your problem.
I like it (using Newtonsoft):
JObject data = (JObject)JsonConvert.DeserializeObject(json);
var attach1_type = data.Descendants()
.OfType<JProperty>()
.FirstOrDefault(x => x.Name == "attach1_type")
?.Value;
numbers, strings, objects are in same array, which means they are boxed before returning to you. so your updates is List<List<object>> or object[][], whatever. your c# class, which match this json format, could be simply like this:
public class SomethingJsonResult
{
public int ts { get; set; }
public List<List<object>> updates { get; set; }
}
The 1st option is to use anonymous type:
public void ParseJsonResult(SomethingJsonResult result)
{
var definition = new
{
attach1_type = "",
attach1 = "",
title = ""
};
result?.updates?.ForEach(x =>
{
var update = JsonConvert.DeserializeAnonymousType(x[6], definition);
var attachment = new Attachment
{
AttachType = update.attach1_type,
Attach = update.attach1,
Title = update.title,
};
});
}
The 2nd option is a bit complex:
[DataContract()]
public class Attachment
{
[DataMember(Name = "attach1_type")]
[JsonProperty("attach1_type")] //Tell JsonConverter how to map your object
public string AttachType { get; set; }//Here is property, but not variable
[DataMember(Name = "attach1")]
[JsonProperty("attach1")]
public string Attach { get; set; }
[DataMember(Name = "title")]
[JsonProperty("title")]
public string Title { get; set; }
}
public void ParseJsonResult(SomethingJsonResult result)
{
result?.updates?.ForEach(update =>
{
//(Attachment)update[6] works only when your names of properties 100% match json objects
JsonConvert.DeserializeObject<Attachment>(update[6])
....
});
}
DataContractSerializer example:
https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.datamemberattribute?redirectedfrom=MSDN&view=netframework-4.7.2
you can implement everything inside your custom converter:
https://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm
JsonConvert.DeserializeObject<AttachmentWrapper>(json, new AttachmentConverter(typeof(AttachmentWrapper)));
I am trying to parse a JSON response from a SharePoint list and parse certain nodes to a list. There can be multiple records. I am using Newsoft Json to deserialize the response. Everything works expect the foreach loop I created iterates through the results one time. Also in the foreach loop the items variable has data as the results variable.
Here is the code:
static List<string> get_json_unsubscribe()
{
int counter = 0;
List<string> list_of_json_users = new List<string>();
String url = "http://server/site/_api/lists/getbytitle('listname')"
HttpWebRequest endpointRequest = (HttpWebRequest)HttpWebRequest.Create(url);
endpointRequest.Credentials = CredentialCache.DefaultCredentials;
endpointRequest.Method = "GET";
endpointRequest.Accept = "application/json;odata=verbose";
HttpWebResponse endpointResponse = (HttpWebResponse)endpointRequest.GetResponse();
Stream receiveStream = endpointResponse.GetResponseStream();
StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8);
dynamic results = JsonConvert.DeserializeObject<dynamic>(readStream.ReadToEnd());
foreach (var item in results)
{
string userid= results.d.results[counter].userid
list_of_json_users.Add(userid);
counter++;
}
return list_of_json_users;
}
The endpoint:
http://server/site/_api/lists/getbytitle('<listtitle>')
returns List resource properties, you probably want this endpoint:
http://server/site/_api/lists/getbytitle('<listtitle>')/items
to return list data (ListItemCollection resource)
To deserialize your JSON you could introduce a model:
public class UserInfo
{
[JsonProperty("AuthorId")]
public string AuthorId { get; set; }
}
public class UserInfoResult
{
[JsonProperty("results")]
public List<UserInfo> UserInfoCollection { get; set; }
}
public class UserInfoResultRoot
{
[JsonProperty("d")]
public UserInfoResult Result { get; set; }
}
Note: in the provided example AuthorId property of ListItem
resource is marked for deserialization
In that case deserializing of list items:
dynamic results = JsonConvert.DeserializeObject<dynamic>(readStream.ReadToEnd());
foreach (var item in results)
{
string userid= results.d.results[counter].userid
list_of_json_users.Add(userid);
counter++;
}
could be replaced with:
var json = readStream.ReadToEnd();
var data = JsonConvert.DeserializeObject<UserInfoResultRoot>(json);
list_of_json_userscould be initialized like this:
list_of_json_users = data.Result.UserInfoCollection.Select(ui => ui.AuthorId).ToList();
Fairly new to working with Json and having troubles
[{"page":1,"example":
[{"number":6666666,"Year":2005}]},
{"page":2,"example":
[{"number":555555,"Year":2000}]}]
This is my Json, it's just an example and not actually Json that i'm using but set out the same way
I been using the following c# to get the values within page 1 of the Json but i need help getting the values from page 2 and so forth
var http = new HttpClient();
var response = await http.GetAsync("Example.json");
var result = await response.Content.ReadAsStringAsync();
List<Rootobject> RootList = JsonConvert.DeserializeObject<List<Rootobject>>(result);
foreach (Rootobject item in RootList)
{
listBox1.Items.Add(item.Example[0].number.ToString());
}
Lastly my Classes are
public class Thread
{
[JsonProperty("number")]
public int number { get; set; }
[JsonProperty("year")]
public int year { get; set; }
}
public class Rootobject
{
[JsonProperty("page")]
public int page { get; set; }
[JsonProperty("example")]
public List<Example> example{ get; set; }
}
You can use LINQ expression
var data = var i in RootList
where i.page == 2; // here you can replace the number as per your requirement
Method to return required data
Public Rootobject GetDataByPage(int pageNo) {
return RootList.FirstOrDefault(x => x.page == pageNo);
}