Extract properties from Newtonsoft jObject - c#

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)
{
// ...
}

Related

Unable to cast object of type 'System.Collections.Generic.List`1[MW01.Web.Model.Countries]' to type 'MW01.Web.Model.Country'.'

I am trying to deserialize an object from API but I keep getting either cast error.
Here is my Country class:
public class Country
{
public List<Countries> country { get; set; }
}
public class Countries
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("alpha3code")]
public string Alpha3Code { get; set; }
[JsonProperty("capital")]
public string Capital { get; set; }
[JsonProperty("region")]
public string Region { get; set; }
[JsonProperty("subregion")]
public string SubRegion { get; set; }
[JsonProperty("borders")]
public string[] Borders { get; set; }
}
This is my controller method:
private void CreateCountries(Country model)
{
IContentService cs = Services.ContentService;
var parentNode = cs.GetById(1097);
var parentUdi = new GuidUdi(parentNode.ContentType.ToString(), parentNode.Key);
Country listofQuery = WikiData.GetCountries();
List<Countries> queryList = new List<Countries>();
foreach (Countries countries in listofQuery.country)
{
queryList.Add(countries);
}
Thread.Sleep(500);
}
points to my WikiData class:
public static Country GetCountries()
{
string feed = "https://ajayakv-rest-countries-v1.p.rapidapi.com/rest/v1/all/?rapidapi-key=be78b7ef9cmshba4472195aeedbep1bf589jsncac9cc382cca";
return serialized_CountryData<Country>(feed);
}
And on to:
public static T serialized_CountryData<T>(string url) where T : new()
{
using (var w = new WebClient())
{
w.Encoding = System.Text.Encoding.UTF8;
var json_data = string.Empty;
try
{
json_data = w.DownloadString(url);
}
catch (Exception)
{
}
//return !string.IsNullOrEmpty(json_data) ? JsonConvert.DeserializeObject<T>(json_data) : new T();
IEnumerable<Countries> result = JsonConvert.DeserializeObject<IEnumerable<Countries>>(json_data);
return (T)result;
}
}
But after this I get the error "Unable to cast object of type 'System.Collections.Generic.List`1[MW01.Web.Model.Countries]' to type 'MW01.Web.Model.Country'."
I've tried many different approaches to this issue and they all result in some sort of cast error or object definition error. Any idea how to solve this?
EDIT:
When attempting to do this (which feels like it should work):
public static Country GetCountries()
{
string feed = "https://ajayakv-rest-countries-v1.p.rapidapi.com/rest/v1/all/?rapidapi-key=be78b7ef9cmshba4472195aeedbep1bf589jsncac9cc382cca";
var testing = serialized_CountryData<Country>(feed);
return testing;
}
and
public static T serialized_CountryData<T>(string url) where T : new()
{
using (var w = new WebClient())
{
w.Encoding = System.Text.Encoding.UTF8;
var json_data = "";
try
{
json_data = w.DownloadString(url);
}
catch (Exception)
{
}
var result = JsonConvert.DeserializeObject<T>(json_data);
return result;
}
}
This builds just fine but when loading the website I get:
"Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'MW01.Web.Model.Country' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array."
And I'm not sure how to solve that.
In controller
Replace
return serialized_CountryData<Country>(feed);
with
return serialized_CountryData<IEnumerable<Country>>(json_data);
Or replace
return serialized_CountryData<Country>(feed);
with
return serialized_CountryData<Country>(json_data);
Fix 1:
You are returning an IEnumerable in a function that wants a Countries object. See comment I added to your code:
// this function returns a list
public static T serialized_CountryData<T>(string url) where T : new()
{
using (var w = new WebClient())
{
w.Encoding = System.Text.Encoding.UTF8;
var json_data = string.Empty;
try
{
json_data = w.DownloadString(url);
}
catch (Exception)
{
}
//return !string.IsNullOrEmpty(json_data) ? JsonConvert.DeserializeObject<T>(json_data) : new T();
IEnumerable<Countries> result = JsonConvert.DeserializeObject<IEnumerable<Countries>>(json_data);
return (T)result;
}
}
// but this function definition states it returns a single Country
public static Country GetCountries()
{
string feed = "https://ajayakv-rest-countries-v1.p.rapidapi.com/rest/v1/all/?rapidapi-key=be78b7ef9cmshba4472195aeedbep1bf589jsncac9cc382cca";
using (var w = new WebClient())
{
w.Encoding = System.Text.Encoding.UTF8;
var json_data = string.Empty;
try
{
json_data = w.DownloadString(feed);
}
catch (Exception)
{
}
// but here it returns the output of the above mentioned funciton -> and this is a list
return serialized_CountryData<Country>(feed);
}
}
// therefore you have to change the funciton definition to
public static IEnumerable<Country> GetCountries(){
// content
}
Fix 2:
and as #Useme mentioned you also have to replace
return serialized_CountryData<Country>(feed);
with
return serialized_CountryData<Country>(json_data);
Fix 3:
Please do me the favor and rename Countries to Country and vice versa. Then you won't get confused. Then it is clear when you pass multiple and when you pass single entries. For example:
public List<Country> GetCountries(){}
public Country GetCountry(){}
or with an additional object named Countries it would be:
public Countries GetCountries(){}
public Country GetCountry(){}

Return list from static async Task method

I have a Task method that calls a remote API that takes the returned JSON and converts the data to C# objects, and displays the data on a console. I would like this method to return the list as well so I can consume it elsewhere can someone show me how as I keep on getting error when I change the method signature to a list type, all replies appreciated! -
// asynchronous retrieve data from 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
using (HttpResponseMessage res = await client.GetAsync(baseUrl))
//convert response to c# object
using (HttpContent content = res.Content)
{
//convert data content to string using await
var data = await content.ReadAsStringAsync();
//If the data is not null, parse(deserialize) the data to a C# object
if (data != null)
{
var result = JsonConvert.DeserializeObject<UserList>(data);
foreach (var u in result.Results)
{
Console.WriteLine("Name: {0} | Url: {1}", u.Name, u.Url);
}
}
else
{
Console.WriteLine("No returned data");
}
}
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
}
User.cs -
public class User
{
[Key]
public int UserId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
}
UserList.cs -
public class UserList
{
public List<User> Results { get; set; }
}
public static async Task<UserList> GetUser()
{
//baseUrl
string baseUrl = "http://pokeapi.co/api/v2/pokemon/";
try
{
// HttpClient implements a IDisposable interface
using (HttpClient client = new HttpClient())
{
//initiate Get Request
using (HttpResponseMessage res = await client.GetAsync(baseUrl))
{
//convert response to c# object
using (HttpContent content = res.Content)
{
//convert data content to string using await
var data = await content.ReadAsStringAsync();
//If the data is not null, parse(deserialize) the data to a C# object
if (data != null)
{
return JsonConvert.DeserializeObject<UserList>(data);
}
else
{
return null;
}
}
}
}
}
catch (Exception exception)
{
return null;
}
}
You can use await after, like this:
UserList test = await GetUser();
// asynchronous retrieve data from api
public static async Task<List<User>> GetUser()
{
//Connect to the webservice and get the data
//Parse the data into a list of Users
var myList = parseResultJsonFromServer(serverResult);
//myList is of type List<User> and ready to be returned
return myList
}
the return has to be a Task<List<User>>, not just List<User>

Having trouble converting multi-level JSON string into object list. Any suggestions?

Hey all so here is the JSON string I expect back {\"status\":\"success\",\"locations\":[{\"id\":\"24\",\"name\":\"Test New Location Test\",\"contact_first_name\":\"Test\",\"contact_last_name\":\"Test\",\"contact_email\":\"test#email.com\",\"contact_phone_number\":\"(555) 555-5555\",\"billing_address\":\"Test\",\"billing_city\":\"Test\",\"billing_state\":\"AK\",\"billing_zip\":\"55555\",\"traps\":[]}]}
I am trying to store all the different parts that make up a location to an object list such as id, name, contact_first_name etc.. I think what is tripping me up is the status in front that is making it a little more difficult for me access the different locations.
I am following this tutorial that seems pretty clear but haven't gotten it to work on my end yet. https://www.youtube.com/watch?v=XssLaKDRV4Y
The below code is part of my Service class and it works in getting the expected http response (mentioned above) and getting the success message. When I uncomment the few lines of code below my app breaks and doesn't store any objects to a list.
public async Task<string> GetLocationData()
{
var user_id = Convert.ToString(App.Current.Properties["user_id"]);
var session = Convert.ToString(App.Current.Properties["session"]);
var key = "randomkeystring";
var body = new List<KeyValuePair<string, string>>();
body.Add(new KeyValuePair<string, string>("user_id", user_id));
body.Add(new KeyValuePair<string, string>("session", session));
body.Add(new KeyValuePair<string, string>("key", key));
try
{
using (var client = new HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Post, "apiurl/api/something") { Content = new FormUrlEncodedContent(body) };
var result = await client.SendAsync(request);
if (!result.IsSuccessStatusCode)
{
return "false";
}
//string representation
var stringResponseFromServer = await result.Content.ReadAsStringAsync();
//convert JSON to series of objects
//LocationCollection locationCollection = JsonConvert.DeserializeObject<LocationCollection>(stringResponseFromServer);
//System.Diagnostics.Debug.WriteLine(locationCollection.locations.Count);
var response = JsonConvert
.DeserializeObject<GetLocationDataResponse>(stringResponseFromServer);
if (response == null) return "false";
jsonString.HttpGetLocationDataString += stringResponseFromServer;
return stringResponseFromServer;
}
}
catch
{
return "false";
}
}
My locations.cs looks like this
public class Locations
{
public int id { get; set; }
public string name { get; set; }
public string contact_first_name { get; set; }
public string contact_last_name { get; set; }
public string contact_email { get; set; }
public string contact_phone_number { get; set; }
public string billing_address { get; set; }
public string billing_city { get; set; }
public string billing_state { get; set; }
public string billing_zip { get; set; }
public string traps { get; set; }
}
Then I have a LocationCollection.cs where i hope to store the different locations so I can loop through them later and do whatever I need to do to them.
public class LocationCollection
{
public List<Locations> locations { get; set; }
}
And then I call the method on my MainPage after the user logs in
insectService.GetLocationData().ContinueWith(async (task) =>
{
var getLocationDataResponse = JsonConvert.DeserializeObject<GetLocationDataResponse>(task.Result);
if (getLocationDataResponse.status == "failure")
{
await DisplayAlert("Location Data Failure", "Could not retrieve data", "Try Again");
await Navigation.PushModalAsync(new LoginPage(), true);
}
//System.Diagnostics.Debug.WriteLine(getLocationDataResponse.locations.ToString());
if (getLocationDataResponse.status == "success")
{
await DisplayAlert("Location Data Success", "Successfully Recovered Data", "Back to Main Page");
}
}); //TaskScheduler.FromCurrentSynchronizationContext());
Right now I am able to get the expect JSON string of {\"status\":\"success\",\"locations\":[{\"id\":\"24\",\"name\":\"Test New Location Test\",\"contact_first_name\":\"Test\",\"contact_last_name\":\"Test\",\"contact_email\":\"test#email.com\",\"contact_phone_number\":\"(555) 555-5555\",\"billing_address\":\"Test\",\"billing_city\":\"Test\",\"billing_state\":\"AK\",\"billing_zip\":\"55555\",\"traps\":[]}]} and am able to check if the status is success or failure. However I am having trouble storing the different parts of "locations" into a list. Any suggestions?
You can give a try deserilizing your api result in to a result model, then from there again de serialize to location model. Example:
My API Model
public class ApiResult
{
public Int32 Status { get; set; }
public string Message { get; set; }
public string Data { get; set; }
}
Inside Data I copy all my return result from API, then Deserialize to exact Model. Here is the example:
public static List<Models.OrderList> RetrieveOrderList(string host, List<Models.FilterCondition> filter)
{
string sResult = HttpHelper.httpPost(host + "api/Order/RetrieveOrderList", Newtonsoft.Json.JsonConvert.SerializeObject(filter));
Models.ApiResult mResult = Newtonsoft.Json.JsonConvert.DeserializeObject<Models.ApiResult>(sResult);
if (mResult.Status == 0)
throw new Exception(mResult.Message);
return Newtonsoft.Json.JsonConvert.DeserializeObject<List<Models.OrderList>>(mResult.Data);
}
If you see the above My return result(string), I deserialize to API result Model, then again finally deserialize to OrderList Model. Hope this help to sort out your issue.
Update: API Controller
I forgot to mention one more point. On the API Controller Side Your result need to copied to API Model.
Here is the Example
[HttpPost]
public Models.ApiResult RetrieveOrderList(List<Models.FilterCondition> conditions)
{
Models.ApiResult mResult = new Models.ApiResult();
try
{
List<Models.OrderList>mOrderList= BLL.Order.RetrieveOrderList(conditions);
mResult.Status = 1;
mResult.Message = "Success";
mResult.Data = Newtonsoft.Json.JsonConvert.SerializeObject(mOrderList);
return mResult;
}
catch (Exception ex)
{
mResult.Status = 0;
mResult.Message = ex.Message;
mResult.Data = "";
return mResult;
}
}
My locations model didn't match the JSON response. Once I read what the exception was in my catch statement I saw that 'traps' was supposed to be another list. After I changed traps property to a List and then made another class for 'traps' everything worked fine.

C# and jSON is not working

Need a little help here
So I have a json url and need to get each item into a for each loop
Here is the json
{
"_links": { },
"count": 9,
"list": {
"staff": [
"staff1",
"staff2",
"staff3"
],
"clients": [
"client1",
"client2",
"client3",
"client4",
"client5",
"client6"
]
}
}
I have also got the following code in c# but keep getting errors
string source;
var sURL = "LINK_TO_JSON_URL";
WebRequest req = HttpWebRequest.Create(sURL);
req.Method = "GET";
req.Timeout = 5000;
try
{
using (StreamReader reader = new StreamReader(req.GetResponse().GetResponseStream()))
{
source = reader.ReadToEnd();
reader.Close();
}
JToken jObject = JObject.Parse(source);
string clients = (string)jObject["list"]["clients"];
//for each loop here
}
catch (Exception ex)
{
//error message here
}
What is it that I am doing wrong? I have tried to convert string to array and still get nothing.
I want to be able to get each clients names
Cheers
So here is some code to show iterating over a JArray that contains strings. This code has been tested with your json and outputs each client string.
var jObject = JObject.Parse(source);
foreach (var client in jObject["list"]["clients"])
{
Console.WriteLine((string)client);
}
Look at this piece of code
#region Model For Deserialize String to Object
public class Links
{
}
public class List
{
public List<string> staff { get; set; }
public List<string> clients { get; set; }
}
public class RootObject
{
public Links _links { get; set; }
public int count { get; set; }
public List list { get; set; }
}
#endregion
#region Restfull Respone (String) Convert To RootObject
public class ConvertStringToObj
{
public void Execute()
{
//Your webReq and Response progress here
string jsonResponse="";
var rootObj = (RootObject)Newtonsoft.Json.JsonConvert.DeserializeObject(jsonResponse, typeof(RootObject));
foreach(var eachItem in rootObj.list.staff)
{
var stafName = eachItem;
}
}
}
#endregion
Here is an solution, you can do this via Objects :)

How to Parse Json from UrI to list on Xamarin Android

I tried too many but no success
this is my method to get JSON string from web service Uri and deserialize it to list, and I want to use it on Xamarin Android App
public async void DownloadDataAsync()
{
string url = "http://myWebSite.com/jWebService.asmx/GetOffersJSON?storeID=2";
var httpClient = new HttpClient();
Task <string> downloadTask = httpClient.GetStringAsync(url);
string content = await downloadTask;
// de-serializing json response into list
JObject jsonResponse = JObject.Parse(content);
IList<JToken> results = jsonResponse["offs"].ToList();
foreach (JToken token in results)
{
offers poi = JsonConvert.DeserializeObject<offers>(token.ToString());
offs.Add(poi);
}
}
when I call DownloadDataAsync(); I get an error:
An unhandled exception occured.
what is the solution?
I've parameter on my web service method, who can I deal with it?
Here is my JSON Uri result:
This XML file does not appear to have any style information associated with
it. The document tree is shown below.
<string xmlns="http://tempuri.org/">[{"ItemID":20,"ItemBarcode":"111","ItemName":"hgh","ItemImage":"MegaOrders22017-04-14-08-34-27.jpg","ItemPrice":7.0000,"ItemNotes":"gffgdfj","OfferOn":true},{"ItemID":21,"ItemBarcode":"222","ItemName":"Nod","ItemImage":"MegaOrders22017-04-14-08-34-57.jpg","ItemPrice":4.0000,"ItemNotes":"kkkkkk","OfferOn":true},{"ItemID":22,"ItemBarcode":"333","ItemName":"kjkjkjkj","ItemImage":"MegaOrders22017-04-14-08-35-21.jpg","ItemPrice":6.0000,"ItemNotes":"hhhhggggg","OfferOn":true},{"ItemID":23,"ItemBarcode":"4444","ItemName":"oioioio","ItemImage":"MegaOrders22017-04-14-08-35-50.jpg","ItemPrice":5.0000,"ItemNotes":"hjhgfdfghj","OfferOn":true}]
</string>
the Class I used:
public class offers
{
public int ItemID { get; set; }
public string ItemBarcode { get; set; }
public string ItemName { get; set; }
public string ItemImage { get; set; }
public double ItemPrice { get; set; }
public string ItemNotes { get; set; }
public bool OfferOn { get; set; }
}
Please try this:
public async void DownloadDataAsync()
{
try
{
string url = "http://myWebSite.com/jWebService.asmx/GetOffersJSON?storeID=2";
var httpClient = new HttpClient();
var content = await httpClient.GetStringAsync(url);
// de-serializing json response into list, with filtering before
var startPosition = content.IndexOf('>') + 1;
var endPosition = content.LastIndexOf("</", StringComparison.Ordinal);
var filteredResponseCharArray = new char[endPosition - startPosition];
content.CopyTo(startPosition, filteredResponseCharArray, 0, endPosition - startPosition);
var listOfOffers = JsonConvert.DeserializeObject<List<offers>>(new string(filteredResponseCharArray));
}
catch (Exception error)
{
Debug.WriteLine(error);
throw;
}
}
You should change your web service to get a valid JSON response without XML structure.

Categories