Parsing Dynamic Json datastructure to object C# / dotnet core - c#

I am trying to parse data from Riot API to C# objects
https://developer.riotgames.com/api-methods/#lol-static-data-v1.2/GET_getChampionList
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("https://global.api.riotgames.com/api/lol/static-data/EUW/v1.2/champion?champData=allytips%2Cenemytips%2Cimage%2Ctags&dataById=true&api_key=###################");
if (response.IsSuccessStatusCode)
{
var streamTask = response.Content.ReadAsStreamAsync();
var stringJson = await response.Content.ReadAsStringAsync();
var serializer = new DataContractJsonSerializer(typeof(ChampionWrapper));
var champions = serializer.ReadObject(await streamTask) as ChampionWrapper;
}
So far I am only getting the type and version converted.
The Data structure looks as following:
{
"type": "champion",
"version": "7.9.1",
"data": {
"89": {
"id": 89,
"key": "Leona",
"name": "Leona",
"title": "the Radiant Dawn",
"image": {
"full": "Leona.png",
"sprite": "champion2.png",
"group": "champion",
"x": 0,
"y": 0,
"w": 48,
"h": 48
},
"allytips": [
"Lead the charge and mark your foes with Sunlight before your allies deal damage.",
"Shield of Daybreak and Zenith Blade form a powerful offensive combo.",
"You can absorb a huge amount of damage using Eclipse, but you must stay near enemies to gain the bonus duration."
],
"enemytips": [
"When Leona activates Eclipse, you have three seconds to get away from her before she deals damage.",
"Only foes in the center of Solar Flare get stunned, so you can often avoid this if you're quick."
],
"tags": [
"Tank",
"Support"
]
},
"110": {
"id": 110,
"key": "Varus",
"name": "Varus",
"title": "the Arrow of Retribution",
"image": {
"full": "Varus.png",
"sprite": "champion3.png",
"group": "champion",
"x": 336,
"y": 96,
"w": 48,
"h": 48
},
So far I have a ChampionWrapper:
public class ChampionWrapper
{
public string type { get; set; }
public string version { get; set; }
public List<ChampionData> data { get; set; }
public ChampionWrapper()
{
}
}
List ChampionData is not getting populated. Version and type is working.
public List<string> id {get;set;}
public List<Champion> id { get; set; }
public ChampionData()
{
}
Here is the champion object
public string Id { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public List<string> AllyTips { get; set; }
public List<string> EnemyTips { get; set; }
public List<string> Tags { get; set; }
public Champion()
{
}
My main problem is the dynamic datastruce
"data": {
"89": { .... }
"110": { .... }
The number is just the ID of the champion.

Found solution:
DataContractJsonSerializerSettings settings = new DataContractJsonSerializerSettings();
settings.UseSimpleDictionaryFormat = true;
The whole thing:
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("https://global.api.riotgames.com/api/lol/static-data/EUW/v1.2/champion?champData=allytips%2Cenemytips%2Cimage%2Ctags&dataById=true&api_key=###########");
if (response.IsSuccessStatusCode)
{
DataContractJsonSerializerSettings settings = new DataContractJsonSerializerSettings();
settings.UseSimpleDictionaryFormat = true;
var streamTask = response.Content.ReadAsStreamAsync();
var stringJson = await response.Content.ReadAsStringAsync();
var serializer = new DataContractJsonSerializer(typeof(ChampionWrapper), settings);
var champions = serializer.ReadObject(await streamTask) as ChampionWrapper;
}
And ofcourse changing list to Dictionary:
public class ChampionWrapper
{
public string type { get; set; }
public string version { get; set; }
public Dictionary<string, Champion> data { get; set; }
}

You are not using the correct class for deserializing your object. The data property in your json is not a List, but more like a Dictionary:
public class ChampionWrapper
{
public string type { get; set; }
public string version { get; set; }
public Dictionary<string, Champion> data { get; set; }
}
[Edit]
I believe that you are complicating the way to deserialize the response from Json, instead of deserializing the response manually using DataContractSerializer you should use ReadAsAsync<T> extension method to obtain an object of your class:
if(response.IsSuccessStatusCode)
{
var champions = await response.Content.ReadAsAsync<ChampionWrapper>();
}
This extension method is found inside the Microsoft.AspNet.WebApi.Client nuget package, remember to add it before using it.

Related

How to access multiple groups of nested JSON elements in C#?

I have a JSON being received from a public API that follows the structure below.
[
{
"ID": "12345",
"company": [
{
"contract_ID": "abc5678",
"company": [
{
"company_name": "HelloWorld",
"company_logo": "HW",
"invested": "2000"
}
]
},
{
"contract_ID": "67891",
"company": [
{
"company_name": "GoodBye",
"company_logo": "GB",
"invested": "500"
}
]
},
{
"contract_ID": "32658",
"company": [
{
"company_name": "YesNo",
"company_logo": "YN",
"invested": "1500"
}
]
}
]
}
]
I've tried several different methods of parsing, whether it be JTokens/JArrays or various classes. Something like the following allows me to access the first group of values (company_name, company_logo, invested), but I cannot iterate through to find the other two.
//receiving the json data
var responseString = await response.Content.ReadAsStringAsync();
var fullJSON = JsonConvert.DeserializeObject(responseString);
Debug.Log(fullJSON);
//trying to parse it down to just the data I need
JToken token = JToken.Parse("{\"root\":" + responseString + "}");
Debug.Log(token);
JArray assets = (JArray)token.SelectToken("root[0].assets");
Debug.Log(assets);
JToken dig = JToken.Parse("{\"root\":" + assets + "}");
Debug.Log(dig);
JArray assetsNested = (JArray)dig.SelectToken("root[0].assets");
Debug.Log(assetsNested);
My goal is to extract the contract_ID and then the associated company_name, company_logo, and invested items. For example, abc5678, HelloWorld, HW, and 2000 would be one of the three datasets needed.
The easiest way to deserialize this properly and keep your code maintainable is to use concrete classes. For example:
public sealed class Company
{
[JsonProperty("contract_ID")]
public string ContractID { get; set; }
[JsonProperty("company")]
public List<CompanyDefinition> CompanyDefinitions { get; set; }
}
public sealed class CompanyDefinition
{
[JsonProperty("company_name")]
public string CompanyName { get; set; }
[JsonProperty("company_logo")]
public string CompanyLogo { get; set; }
[JsonProperty("invested")]
public string Invested { get; set; }
}
public sealed class RootCompany
{
[JsonProperty("ID")]
public string ID { get; set; }
[JsonProperty("company")]
public List<Company> Company { get; set; }
}
Then, you simply deserialize. For example, if you are pulling from a file you could do this:
using(var sr = new StreamReader(#"c:\path\to\json.json"))
using(var jtr = new JsonTextReader(sr))
{
var result = new JsonSerializer().Deserialize<RootCompany[]>(jtr);
foreach(var r in result)
{
Console.WriteLine($"Company ID: {r.ID}");
foreach(var c in r.Company)
{
Console.WriteLine($"ContractID: {c.ContractID}");
foreach(var d in c.CompanyDefinitions)
{
Console.WriteLine($"Name: {d.CompanyName}");
Console.WriteLine($"Invested: {d.Invested}");
Console.WriteLine($"Company Logo: {d.CompanyLogo}");
}
}
}
}
When something changes with your models, you won't have to dig through lines upon lines of hard-to-read token selection code. You just update your models.

JSON convert array type to list in C#

I have some array data coming from an API in JSON format, and I want to convert an array type to a list. There is an ASP.NET MVC project and I used the list in Index page. How can I deserialize the array format into a list?
Controller
public async Task<IActionResult> ListCountries()
{
List<Country> countries = new List<Country>();
HttpClient _client = new HttpClient();
HttpResponseMessage _response = new HttpResponseMessage();
_client = _apiHelper.Initial();
_response = await _client.GetAsync("api/Countries/getall");
if (_response.IsSuccessStatusCode)
{
var results = _response.Content.ReadAsStringAsync().Result;
countries = JsonConvert.DeserializeObject<List<Country>>(results);
}
return View(countries);
}
Data
"data": [
{
"id": 1,
"countryName": "Afghanistan"
},
{
"id": 2,
"countryName": "Albania"
},
{
"id": 3,
"countryName": "Algeria"
},
Entity
public class Country
{
[Key]
public int Id { get; set; }
public string CountryName { get; set; }
}
Your json data is invalid format. Json maybe have to be like this:
{
"data": [
{
"id": 1,
"countryName": "Afghanistan"
},
{
"id": 2,
"countryName": "Albania"
},
{
"id": 3,
"countryName": "Algeria"
}
]
}
After that you should create 2 c# class like this:
public class JsonData
{
public List<Country> data { get; set; }
}
public class Country
{
public int id { get; set; }
public string countryName { get; set; }
}
Then you can deserialize it without any error.
public async Task<IActionResult> ListCountries()
{
List<Country> countries = new List<Country>();
HttpClient _client = new HttpClient();
HttpResponseMessage _response = new HttpResponseMessage();
_client = _apiHelper.Initial();
_response = await _client.GetAsync("api/Countries/getall");
if (_response.IsSuccessStatusCode)
{
var results = _response.Content.ReadAsStringAsync().Result;
countries = JsonConvert.DeserializeObject<JsonData>(results);
}
return View(countries);
}
The object has a variable data that contains the countries whereas the class you're trying to serialize to does not.
Having a class such as:
public class Country{
[JsonProperty('data')]
public List<data> Details {get;set;}
public Country(){
Details = new List<data>();
}
public class data{
[Key]
[JsonProperty('id')]
public int Id { get; set; }
[JsonProperty('countryName')]
public string CountryName { get; set; }
}
and then to deserialize it you would need:
countries = JsonConvert.DeserializeObject<Country>(results);
When it deserializes the object it will map data to the Details variable in the class Country and map each value in the response array to the data class
The structure shown is not a list/array of Countries but an object which has a property data which is a list/array of Contries:
public class Result
{
public List<Country> Data {get; set;}
}
...
var r = JsonConvert.DeserializeObject<Result>(results);
var countries = r.Data;
Minor note. Since you're using Json.NET it's OK that your properties don't match the case in json, but if you switch to System.Text.Json this would become an issue.
you have to parse and use JArray inside of your result
var countries = JObject.Parse(results)["data"].ToObject<List<Country>>();
// data for test
var results = #"{""data"": [
{
""id"": 1,
""countryName"": ""Afghanistan""
},
{
""id"": 2,
""countryName"": ""Albania""
},
{
""id"": 3,
""countryName"": ""Algeria""
}]}";

Returning Array of response from Web API C#

I am trying to create a web API which calls the other service and returns a array of response. The called service returns the response. I am able to get the individual item from the called service. But not sure how to build array of items and return as response from the API I am creating.
The JSON returned from the service looks like
{
"cr_response": {
"details": [{
"name": "Req",
"fields": [{
"value": "Prj0\r\nPrj1",
"name": "Project"
},
{
"value": "October 13, 2017 14:18",
"name": "Submitted"
},
{
"value": "John",
"name": "Rec Name"
}
]
}],
"cr_metadata": {}
}
}
And the POCO class looks like
public class Field
{
public string value { get; set; }
public string name { get; set; }
}
public class Detail
{
public string name { get; set; }
public List<Field> fields { get; set; }
}
public class CrMetadata
{
}
public class CrResponse
{
public List<Detail> details { get; set; }
public CrMetadata cr_metadata { get; set; }
}
public class RootObject
{
public CrResponse cr_response { get; set; }
}
Below is the code for calling the service and retrieving the response from the services
var response = await iLab_client.GetAsync(uri);
var datafile = await response.Content.ReadAsStringAsync();
var returnDataObj = JsonConvert.DeserializeObject<DTO.RootObject>(datafile);
foreach (var form in returnDataObj.cr_response.details)
{
name_response = form.name;
return Ok(name_response);
}
Here I can access the name from the details but not sure how can I access the all the name and value from the fields and construct it in a array. And send it as a JSON response.
I tried like
foreach (var form in returnDataObj.cr_response.details)
{
var id_response = form.fields;
return Ok(id_response);
}
But it throws error like
<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
The 'ObjectContent`1' type failed to serialize the response body for content
type 'application/xml; charset=utf-8'.
</ExceptionMessage>
System.InvalidOperationException
To return array from Web API you need to fill your array and return it outside foreach loop:
var list = new List<string>();
foreach (...){
var name = ...
list.Add(name);
}
return Ok(list.ToArray()); // or just return Ok(list);
This is how to deserialize JSON to POCO and get the list of the names:
[TestMethod]
public void TestJsonToPocoAndGetNames()
{
const string Json = #"
{
""cr_response"": {
""details"": [{
""name"": ""Req"",
""fields"": [{
""value"": ""Prj0\r\nPrj1"",
""name"": ""Project""
},
{
""value"": ""October 13, 2017 14:18"",
""name"": ""Submitted""
},
{
""value"": ""John"",
""name"": ""Rec Name""
}
]
}],
""cr_metadata"": {}
}
}
";
var settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
};
var response = JsonConvert.DeserializeObject<RootJsonObject>(Json, settings);
var names = new List<string>();
foreach (var detail in response.CrResponse.Details)
{
names.Add(detail.Name);
foreach (var field in detail.Fields)
{
names.Add(field.Name);
}
}
Assert.AreEqual(
"Req, Project, Submitted, Rec Name",
string.Join(", ", names.ToArray()));
}
POCO classes:
public class RootJsonObject
{
[JsonProperty("cr_response")]
public CrResponse CrResponse { get; set; }
}
public class CrResponse
{
[JsonProperty("cr_metadata")]
public CrMetadata CrMetadata { get; set; }
[JsonProperty("details")]
public Detail[] Details { get; set; }
}
public class CrMetadata
{
}
public class Detail
{
[JsonProperty("fields")]
public Field[] Fields { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
public class Field
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("value")]
public string Value { get; set; }
}

How to get only "intent" field from LUIS returned JSON result using C#

how do I get only the "intent" field from the JSON format code from LUIS return result in C#?
I have already done deserialization like this:
public class ResponseContent
{
[BsonId]
public string query { get; set; }
public string topScoringIntent { get; set; }
public string intent { get; set; }
public string entities { get; set; }
}
public class topScoringIntent
{
public string intent { get; set; }
public string score { get; set; }
}
And this is the LUIS return result:
"query": "Hello.",
"topScoringIntent":
{
"intent": "Greeting",
"score": 0.9447609
},
"intents":
[
{
"intent": "Greeting",
"score": 0.9447609
},
{
"intent": "Request",
"score": 0.09726282
},
{
"intent": "None",
"score": 5.523394E-10
}
],
"entities": []
And I only want the "intent" field from the "TopScoringIntent". How can I get that using C#? Below is the code that I tried but nothing came out:
var uri = "https://southeastasia.api.cognitive.microsoft.com/luis/v2.0/apps/" + luisAppId + "?" + queryString;
var response = await client.GetAsync(uri);
var responseContent = await response.Content.ReadAsStringAsync();
var responseitem = JsonConvert.SerializeObject(responseContent,Formatting.Indented);
JToken content = JToken.Parse(responseitem);
var intentOnly = (from s in content.Children()["ResponseContent"]select s).ToList();
foreach (var item in intentOnly.Children().ToList())
{
Console.WriteLine(item.ToObject<ResponseContent>().TopScoringIntent);
}
This is not a bot so I am not using any bot framework to do this.
Thank you.
Get it done by using the following code:
var jsonresponse = JObject.Parse(responseContent);
intentonly = jsonresponse.SelectToken("intents[0].intent").ToString();

How to get the values from list of objects in c#

I have Called a Json Web Service and got the result in c#.The Json Web service data is available in format:
{
"Count": 9862,
"Items": [
{
"Admin": {
"S": "false"
},
"UserId": {
"S": "e9633477-978e-4956-ab34-cc4b8bbe4adf"
},
"Age": {
"N": "76.24807963806055"
},
"Promoted": {
"S": "true"
},
"UserName": {
"S": "e9633477"
},
"Registered": {
"S": "true"
}
},
{
"Admin": {
"S": "false"
},
"UserId": {
"S": "acf3eff7-36d6-4c3f-81dd-76f3a8071bcf"
},
"Age": {
"N": "64.79224276370684"
},
"Promoted": {
"S": "true"
},
"UserName": {
"S": "acf3eff7"
},
"Registered": {
"S": "true"
}
},
I have got the Response like this in c#:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost:8000/userdetails");
try
{
WebResponse response = request.GetResponse();
using (Stream responseStream = response.GetResponseStream())
{
StreamReader reader = new StreamReader(responseStream, Encoding.UTF8);
return reader.ReadToEnd();
}
}
after finally successfully get the response i have got all the Data in string. and then parse this string in list of objects .Now I have list of objects where it showing the count in debugging.Now I want to access the values like UserId:acf3eff7-36d6-4c3f-81dd-76f3a8071bcf like properties.I dont know how to do it.Please help me and any help will be appreciated.
You can use the following code to get the values from json as:
JObject obj = JObject.Parse(json);
int count = (int)obj["Count"];
var Items = obj["Items"];
foreach (var item in Items)
var admin = item["Admin"];
Quick and dirty way:
//deserialize your string json using json.net
dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
//get value of UserId in first Item
var UserId = jsonObj["Items"][0]["UserId"]["S"];
//OR get the value of UserId for each Item in Items
//foreach(dynamic item in jsonObj["Items"])
//item["UserId"]["S"];
Advice is to use c# objects as mentioned by #yousuf
To be able to access Json property like common C# object property, you need to deserialize json string to strongly typed object (you can use, for example, JSON.NET to do deserialization).
Another handy tool is http://json2csharp.com/. Paste your Json there then you can generate classes definitions that suitable to map the Json automatically :
//RootObject class definition generated using json2csharp.com
//the rest of class definition removed for brevity.
public class RootObject
{
public int Count { get; set; }
public List<Item> Items { get; set; }
}
........
........
//in main method
var jsonString = .....;
//deserialize json to strongly-typed object
RootObject result = JsonConvert.DeserializeObject<RootObject>(jsonString);
foreach(var item in result.Items)
{
//then you can access Json property like common object property
Console.WriteLine(item.UserId.S);
}
you are deserializing string to c# object. you will need to create object that reperesents the json .
For example -
public class Admin
{
public string S { get; set; }
}
public class UserId
{
public string S { get; set; }
}
public class Age
{
public string N { get; set; }
}
public class Promoted
{
public string S { get; set; }
}
public class UserName
{
public string S { get; set; }
}
public class Registered
{
public string S { get; set; }
}
public class RootObject
{
public Admin Admin { get; set; }
public UserId UserId { get; set; }
public Age Age { get; set; }
public Promoted Promoted { get; set; }
public UserName UserName { get; set; }
public Registered Registered { get; set; }
}
Then deserialize json string to object using jsonSerializer
JavaScriptSerializer serializer = new JavaScriptSerializer();
var result =
(RootObject)serializer .DeserializeObject("Json String")
string json = #"{
""Name"": ""Apple"",
""Expiry"": new Date(1230422400000),
""Price"": 3.99,
""Sizes"": [
""Small"",
""Medium"",
""Large""
]
}";
JObject o = JObject.Parse(json);
//This will be "Apple"
string name = (string)o["Name"];

Categories

Resources