Coming here after learning about C# classes Constructors and ArrayLists so that not to put a completely dumb question here :)
I'm trying to Deserialize below Nested Lists of JSON returned from an API GET call as below:
I've been able to get the value from the empArra (Field: Emp), but subsequent lists like yearArray, prod and sale are not returning there values.
Can you please look into the below code that where is it doing wrong?
JSON
[
{
"employee":"156718100",
"availability":[
{
"year":2018,
"sales":{
"availability":"Maybe",
"reason":""
},
"prod":{
"availability":"Maybe",
"reason":""
}
},
{
"year":2019,
"sales":{
"availability":"Maybe",
"reason":""
},
"prod":{
"availability":"Maybe",
"reason":""
}
},
{
"year":2020,
"sales":{
"availability":"Maybe",
"reason":""
},
"top":{
"availability":"Maybe",
"reason":""
}
},
{
"year":2021,
"sales":{
"availability":"Maybe",
"reason":""
},
"prod":{
"availability":"Maybe",
"reason":""
}
}
]
}
]
Classes
public class sale
{
public string SaleAvailability { get; set; }
public string SaleReason { get; set; }
public sale(string pSaleAvailability, string pSaleReason)
{
this.SaleAvailability = pSaleAvailability;
this.SaleReason = pSaleReason;
}
public override string ToString()
{
return string.Format("{0} {1}", SaleAvailability, SaleReason);
}
}
public class prod
{
public string ProdAvailability { get; set; }
public string ProdReason { get; set; }
public prod(string pProdAvailability, string pProdReason)
{
this.ProdAvailability = pProdAvailability;
this.ProdReason = pProdReason;
}
public override string ToString()
{
return string.Format("{0} {1}", ProdAvailability, ProdReason);
}
}
public class yearArray
{
public int Year { get; set; }
public yearArray(int pYear)
{
this.Year = pYear;
}
List<sale> Sale { get; set; } = new List<sale>();
List<prod> Prod { get; set; } = new List<prod>();
}
public class rootAvailability
{
public List<yearArray> YearArray { get; set; } = new List<yearArray>();
}
public class empArray
{
public string Emp { get; set; }
public List<rootAvailability> RootAvailability { get; set; } = new List<rootAvailability>();
}
public class rootArray
{
public List<empArray> EmpArrays { get; set; } = new List<empArray>();
}
main() method
(After getting the response from API)
IRestResponse response = client.Execute<rootArray>(request);
//Console.WriteLine(response.Content);
List<rootArray> rootArrays = JsonConvert.DeserializeObject<List<rootArray>>(response.Content);
List<empArray> empArrays = JsonConvert.DeserializeObject<List<empArray>>(response.Content);
List<rootAvailability> rootAvailabilities = JsonConvert.DeserializeObject<List<rootAvailability>>(response.Content);
List<yearArray> yearArrays = JsonConvert.DeserializeObject<List<yearArray>>(response.Content);
List<sale> clsSale = JsonConvert.DeserializeObject<List<sale>>(response.Content);
List<prod> clsProd = JsonConvert.DeserializeObject<List<prod>>(response.Content);
foreach (var rootitem in rootArrays)
{
foreach (var emparrayitem in empArrays)
{
Console.WriteLine("NSN: " + emparrayitem.Emp);
foreach (var rootavailabbilitiesitem in rootAvailabilities)
{
foreach (var yearArrayItem in yearArrays)
{
Console.WriteLine("Year: " + yearArrayItem.Year);
foreach (var saleItem in clsSale)
{
Console.WriteLine("SaleAvailability: " + saleItem.SaleAvailability);
Console.WriteLine("SaleReason: " + saleItem.SaleReason);
}
foreach (var prodItem in clsProd)
{
Console.WriteLine("SaleAvailability: " + prodItem.ProdAvailability);
Console.WriteLine("SaleReason: " + prodItem.ProdReason);
}
}
}
}
}
Results
Emp: 159252663
Year: 0
SaleAvailability:
SaleReason:
SaleAvailability:
SaleReason:
You have two problems with your approach:
You want to deserialize the same source over and over again (response.Content) for different class instances. It can be deserialized into one object type: your top level entity.
Your data model does not reflect your data. For example YearArray should have a single Prod and Sale property not a list of them.
You have several options how to fix it. Let me share with you the two most common ones:
With proper naming
Your object model should look like this:
public class Sale
{
public string Availability { get; set; }
public string Reason { get; set; }
}
public class Prod
{
public string Availability { get; set; }
public string Reason { get; set; }
}
public class MidLevel
{
public int Year { get; set; }
public Sale Sales { get; set; }
public Prod Top { get; set; }
}
public class TopLevel
{
public string Employee { get; set; }
public List<MidLevel> Availability { get; set; } = new List<MidLevel>();
}
Then all you need to do is to call the following command:
var result = JsonConvert.DeserializeObject<TopLevel[]>(json);
Now, your result will be populated with all the data.
With JsonProperty
If you don't want to use the same names in your domain model which is used in the json then you can define the mapping between these two worlds via JsonProperty attributes.
Now your domain model can look like this:
public class SalesInformation
{
[JsonProperty(PropertyName = "availability")]
public string Avail { get; set; }
[JsonProperty(PropertyName = "reason")]
public string Reasoning { get; set; }
}
public class ProdInformation
{
[JsonProperty(PropertyName = "availability")]
public string Availability { get; set; }
[JsonProperty(PropertyName = "reason")]
public string Reasoning { get; set; }
}
public class MidLevel
{
[JsonProperty(PropertyName = "year")]
public int AvailYear { get; set; }
[JsonProperty(PropertyName = "sales")]
public SalesInformation SalesInfos { get; set; }
[JsonProperty(PropertyName = "top")]
public ProdInformation ProdInfos { get; set; }
}
public class TopLevel
{
[JsonProperty(PropertyName = "employee")]
public string Emp { get; set; }
[JsonProperty(PropertyName = "availability")]
public List<MidLevel> Availabilities { get; set; } = new List<MidLevel>();
}
The usage would be exactly the same:
var result = JsonConvert.DeserializeObject<TopLevel[]>(json);
UPDATE: How to display data
To represent hierarchy in a console application can be achieved in may ways. Here I will use indentation. I've introduced the following tiny helper method:
public static void WriteWithIndent(int level, string message) => Console.WriteLine("".PadLeft(level * 2) + message);
With this in hand the data visualization could be achieved in the following way:
var result = JsonConvert.DeserializeObject<TopLevel[]>(json);
foreach (var topLevel in result)
{
Console.WriteLine($"Employee: {topLevel.Emp}");
foreach (var midLevel in topLevel.Availabilities)
{
WriteWithIndent(1, $"Year: {midLevel.AvailYear}");
WriteWithIndent(1, "Sales:");
WriteWithIndent(2, $"Avail: {midLevel.SalesInfos.Avail}");
WriteWithIndent(2, $"Reason: {midLevel.SalesInfos.Reasoning}");
WriteWithIndent(1, "Top:");
WriteWithIndent(2, $"Avail: {midLevel.ProdInfos.Avail}");
WriteWithIndent(2, $"Reason: {midLevel.ProdInfos.Reasoning}");
}
}
The printed output will look like this:
Employee: 156718100
Year: 2018
Sales:
Avail: Maybe
Reason:
Top:
Avail: Maybe
Reason:
Year: 2019
Sales:
Avail: Maybe
Reason:
Top:
Avail: Maybe
Reason:
Year: 2020
Sales:
Avail: Maybe
Reason:
Top:
Avail: Maybe
Reason:
Year: 2021
Sales:
Avail: Maybe
Reason:
Top:
Avail: Maybe
Reason:
c# code:-
var handler = new HttpClientHandler();
HttpClient client = new HttpClient(handler);
string result = await client.GetStringAsync(url);
Console.WriteLine(result);
json respose(result):-
{
"accountstab": [
{
"LoginType": "r",
"RepId": 3368,
"RepName": "Aachi's M",
"RepUName": "aachis",
"RepPwd": "aachis123",
"WhlId": null,
"RepLocalId": null,
"WhoName": "Aachi's M",
"WhoTin": "32661034",
"WhoEmail": "hanee#gmail.com"
},
{
"LoginType": "r",
"RepId": 3335,
"RepName": "AL-NAJA M",
"RepUName": "alnaja",
"RepPwd": "chemmad",
"WhlId": null,
"RepLocalId": null,
"WhoName": "AL-NAJA",
"WhoTin": "7222075",
"WhoEmail": "abbas#gmail.com"
}
]
}
model class:
public class RootObject
{
public List<Accountstab> accountstab { get; set; }
}
public class Accountstab
{
public string LoginType { get; set; }
public int RepId { get; set; }
public string RepName { get; set; }
public string RepUName { get; set; }
public string RepPwd { get; set; }
public int? WhlId { get; set; }
public int? RepLocalId { get; set; }
public string WhoName { get; set; }
public string WhoTin { get; set; }
public string WhoEmail { get; set; }
}
I can be done with Newtonsoft.Json dll from NuGet:
using Newtonsoft.Json;
// ...
RootObject json = JsonConvert.DeserializeObject<RootObject>(result);
Usage example:
foreach (var item in json.accountstab)
Console.WriteLine(item.RepUName);
Output:
aachis
alnaja
if there is any problem with string value="null" in json , public string WhoTin { get; set; } this field are null is some records
The error of you json string is not caused by the null, it caused by the ' in the json below.
"RepName": "Aachi's M",
"WhoName": "Aachi's M",
The way you used to deserliaze the json data in Xamarin.forms is correct.
var jsondata = JsonConvert.DeserializeObject<Rootobject>(json);
When you change the ' in json data, it would be okay.
"RepName": "Aachi s M"
"WhoName": "Aachi s M",
{
"Global Quote": {
"01. symbol": "MSFT",
"02. latest trading day": "2018-11-19",
"03. previous close": "108.2900"}
}
This is the code I m using:
using (var reader = new StreamReader(response.GetResponseStream()))
{
JavaScriptSerializer js = new JavaScriptSerializer();
var objText = reader.ReadToEnd();
//This line is not working.
Stock stock = (Stock)js.Deserialize(objText, typeof(Stock));
return Ok(stock);
}
The stock is model as shown below:
public class Stock
{
public string symbol { get; set; }
public string latesttradingday { get; set; }
public string previousclose { get; set; }
}
All I m getting is Null in all field of Stock. What I m missing here? I m new to JSON.
Use JsonProperty
public class GlobalQuote
{
[JsonProperty("Global Quote")]
public Stock Stock { get; set; }
}
public class Stock
{
[JsonProperty("01. symbol")]
public string symbol { get; set; }
[JsonProperty("02. latest trading day")]
public string latesttradingday { get; set; }
[JsonProperty("03. previous close")]
public string previousclose { get; set; }
}
private static async Task Main(string[] args)
{
string test = #"{
""Global Quote"": {
""01. symbol"": ""MSFT"",
""02. latest trading day"": ""2018-11-19"",
""03. previous close"": ""108.2900""}
}";
var result = JsonConvert.DeserializeObject<GlobalQuote>(test);
}
Full Demo Here
Additional Resources
JsonProperty Class
Maps a JSON property to a .NET member or constructor parameter.
This class I created myself:
public class member
{
public string account_name { get; set; }
public long account_id { get; set; }
public Rootobject[] rootobject { get; set; }
}
This are the classes VS created for me autmatically using an example JSON answer:
public class Rootobject
{
public string status { get; set; }
public Meta meta { get; set; }
public Data data { get; set; }
}
public class Meta
{
public int count { get; set; }
}
public class Data
{
public _507888780[] _507888780 { get; set; }
}
public class _507888780
{
public All all { get; set; }
public int tank_id { get; set; }
}
public class All
{
public int spotted { get; set; }
public int hits_percents { get; set; }
public int wins { get; set; }
...
}
A small part of the JSON response from the API server I use looks like this:
{
"status": "ok",
"meta": {
"count": 1
},
"data": {
"507888780": [
{
"all": {
"spotted": 467,
"hits_percents": 83,
"wins": 281,
},
"tank_id": 2849
},
{
"all": {
"spotted": 224,
"hits_percents": 63,
"wins": 32,
},
"tank_id": 9473
},
}
}
This is the code I use to read out the tanks a member has (including all the stats) where Request(string) is just the http request.
private List<member> memberlist = new List<member>(100);
private void DoStuff()
{
memberlist = JsonConvert.DeserializeObject<List<member>>(result_member);
foreach (var member in memberlist)
{
string result_tank = Request("https://api.worldoftanks.eu/wot/tanks/stats/?application_id=" + application_id + "&account_id=" + member.account_id + "&tank_id=" + tanks + "&fields=all.battles%2C+all.wins%2C+all.damage_dealt%2C+all.frags%2C+all.hits_percents%2C+all.piercings%2C+all.shots%2C+all.spotted%2C+all.survived_battles%2C+all.tanking_factor");
var Rootobject = JsonConvert.DeserializeObject<Rootobject>(result_tank);
foreach (var tank in _507888780)
{
richTextBox1.Text += Rootobject.data._507888780[tank].tank_id + Rootobject.data._507888780[tank].all.spotted.ToString() + "...";
}
}
}
Now, I want to be able to search up all the different tanks including their stats for all members. Right now I'm getting the error in the line I want to print "Type Tank_Statistics._507888780 cannot be implicitly converted to int." Earlier on I alos got an error with a missing IEnumerable which I dont have right now though..
Anyways .. I can't make it work somehow.. it would be very kind if someone would be able to help me on this ;)
Seems that you should replace this
richTextBox1.Text += Rootobject.data._507888780[tank].tank_id + Rootobject.data._507888780[tank].all.spotted.ToString() + "...";
to this
richTextBox1.Text += tank.tank_id + tank.all.spotted.ToString() + "...";
This question already has answers here:
SQLite .NET performance, how to speed up things?
(2 answers)
Closed 7 years ago.
I am having a hard time trying to save the data faster, to a local DB.
Even though this is a one time saving, when the app runs for the first time, it takes like 90 seconds, in a Lumia 920, to save "only" the "map tables".
What I do:
1) I call an API, where I receive all the grids, with its Xs, Ys, Map Id, etc.
2) I deserialize the info based on a class I have defined.
3) For each item, in that info, I save the "misc" info (since I will use it)
4) I save, in a GRIDS table, each grid inside the previous item.
This code snipet is what I use to deserialize the info, and call the function to save in the DB
public class Maps
{
public string id { get; set; }
public string name { get; set; }
public string height { get; set; }
public string width { get; set; }
public string tile { get; set; }
public string shopping_id { get; set; }
public string url { get; set; }
public string updated_at { get; set; }
public string created_at { get; set; }
public GridFirst gridFirst { get; set; }
public GridLast gridLast { get; set; }
public List<Grid> grid { get; set; }
public class GridFirst
{
public string id { get; set; }
public string x { get; set; }
public string y { get; set; }
public string maps_id { get; set; }
public string value { get; set; }
}
public class GridLast
{
public string id { get; set; }
public string x { get; set; }
public string y { get; set; }
public string maps_id { get; set; }
public string value { get; set; }
}
public class Grid
{
public string id { get; set; }
public string x { get; set; }
public string y { get; set; }
public string maps_id { get; set; }
public string value { get; set; }
}
public void deserializeAndConvert(string aaa)
{
JObject myGeneral = JObject.Parse(aaa);
IList<JToken> results = myGeneral["resp"].Children().ToList();
// serialize JSON results into .NET objects
IList<Maps> searchResults = new List<Maps>();
foreach (JToken result in results)
{
Maps searchResult = JsonConvert.DeserializeObject<Maps>(result.ToString());
searchResults.Add(searchResult);
}
var respuesta = from data in searchResults
select new
{
id = data.id,
name = data.name,
height = data.height,
width = data.width,
tile = data.tile,
url = data.url,
lastX = data.gridLast.x,
lastY = data.gridLast.y,
grid = data.grid
};
foreach (var a in respuesta)
{
Database_Controller.getReadyToSaveData("mapinfo", 8, a.id, a.name, a.height, a.width, a.tile, a.url, a.lastX, a.lastY, "", "", "", "", "", "", "");
foreach (var data in a.grid)
{
Database_Controller.getReadyToSaveData("mapgrid", 5, data.id, data.x, data.y, data.maps_id, data.value, "", "", "", "", "", "", "", "", "", "");
}
}
}
}
And these are the functions that save the data, in the DB
public static void getReadyToSaveData(string dbName, int numberOfParams, string param1, string param2, string param3, string param4, string param5, string param6, string param7, string param8, string param9, string param10, string param11, string param12, string param13, string param14, string param15)
{
List<string> myParams = new List<string>();
myParams.Add(param1);
myParams.Add(param2);
myParams.Add(param3);
myParams.Add(param4);
myParams.Add(param5);
myParams.Add(param6);
myParams.Add(param7);
myParams.Add(param8);
myParams.Add(param9);
myParams.Add(param10);
myParams.Add(param11);
myParams.Add(param12);
myParams.Add(param13);
myParams.Add(param14);
myParams.Add(param15);
List<string> myParamsToDB = new List<string>();
for (var i = 0; i < numberOfParams; i++)
{
myParamsToDB.Add(myParams[i]);
}
insertData(dbName, myParamsToDB);
}
public static void insertData(string dbName, List<string> paramsToGo)
{
try
{
using (var connection = new SQLiteConnection("Unicenter.sqlite"))
{
if (dbName == "mapgrid")
{
using (var statement = connection.Prepare(#"INSERT INTO mapgrid (ID,X,Y,MAPS_ID,VALUE)
VALUES(?, ?,?,?,?);"))
{
statement.Bind(1, paramsToGo[0]);
statement.Bind(2, paramsToGo[1]);
statement.Bind(3, paramsToGo[2]);
statement.Bind(4, paramsToGo[3]);
statement.Bind(5, paramsToGo[4]);
// Inserts data.
statement.Step();
statement.Reset();
statement.ClearBindings();
}
}
if (dbName == "mapinfo")
{
using (var statement = connection.Prepare(#"INSERT INTO mapinfo (ID,NAME,HEIGHT,WIDTH,TILE,URL,LASTX,LASTY)
VALUES(?, ?,?,?,?,?,?,?);"))
{
statement.Bind(1, paramsToGo[0]);
statement.Bind(2, paramsToGo[1]);
statement.Bind(3, paramsToGo[2]);
statement.Bind(4, paramsToGo[3]);
statement.Bind(5, paramsToGo[4]);
statement.Bind(6, paramsToGo[5]);
statement.Bind(7, paramsToGo[6]);
statement.Bind(8, paramsToGo[7]);
// Inserts data.
statement.Step();
statement.Reset();
statement.ClearBindings();
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine("Exception\n" + ex.ToString());
}
}
***Edit: As a kind reminder, in case some people does not see the tags (and mark this question as duplicated), this is FOR WINDOWS PHONE 8.1, so, the functions, references and classes ARE different from plain c#
Any idea on how to make it faster? ... What am I doing wrong?
you can use parallel.for loop which is more faster if you have large data or you can easily check each loop how much time it takes to execute in VS-2015