Reputation: 11
I'm making a HTTP request to gain a response body of JSON. The response body is quite large so I've cut the structure down for convenience. My problem is accessing specific data for later use. Then JSON string looks as follows:
{
"0": { <--- ID number
"vk": { <--- Specific object
"cost": 19, <--- information about object
"count": 1903 <--- information about object
},
"ok": {
"cost": 4,
"count": 2863
},
"wa": {
"cost": 4,
"count": 2210
}
}
}
I'm trying to define some sort of class or structure which would allow me to:
But I'm having trouble even separating the data with JSON. I have tried three methods I found on Stack Overflow to not much success, the closest I got was using a token
JToken token = JObject.Parse(response);
and calling token.root to pull the entire block & token.path to pull the ID number. I've seen suggestions about making each field have its own method but there are over 100 "id" brackets which contain upto 50 objects each so that's not really plausible.
I'm asking for assistance on how I would split the JSON data into organised data, I am planning to just create a class to store the data unless theres some specific JSON storage aspect I'm not aware of.
Upvotes: 0
Views: 761
Reputation: 11364
There was the question about sorting, filtering and querying the list of different stores based on either counts or costs. Following is a list pattern that you can use and use LINQ to do the queries and filtering.
public class ItemInfo
{
[JsonProperty("cost")]
public int Cost { get; set; }
[JsonProperty("count")]
public int Count { get; set; }
}
public class AllProdcuts
{
[JsonProperty("vk")]
public ItemInfo VK { get; set; }
[JsonProperty("ok")]
public ItemInfo OK { get; set; }
[JsonProperty("wa")]
public ItemInfo WA { get; set; }
}
public class Stores
{
[JsonProperty("ID")]
public string StoreID { get; set; }
[JsonProperty("store")]
public AllProdcuts Store { get; set; }
}
and this is how you would call it
string jsonDoc = @"{
""0"": {
""vk"": {
""cost"": 19,
""count"": 1903
},
""ok"": {
""cost"": 4,
""count"": 2863
},
""wa"": {
""cost"": 4,
""count"": 2210
}
},
""1"": {
""vk"": {
""cost"": 9,
""count"": 3
},
""ok"": {
""cost"": 4,
""count"": 63
},
""wa"": {
""cost"": 40,
""count"": 210
}
}
}";
var obj = JObject.Parse(jsonDoc);
List<Stores> allStores = new List<Stores>();
foreach (var property in obj.Properties())
{
string storeNumber = property.Name;
allStores.Add(new Stores() { StoreID = property.Name, Store = JsonConvert.DeserializeObject<AllProdcuts>(obj[property.Name].ToString()) });
}
// If you want to get list of <count, cost> for all stores
List<ItemInfo> totalItemInAllStores = allStores.Select(x => x.Store.OK).ToList();
int totalOKInAllStores = allStores.Sum(x => x.Store.OK.Count);
int totalWAInAllStores = allStores.Sum(x => x.Store.WA.Count);
int totalOkInXStore = allStores.FirstOrDefault(x => x.StoreID.Equals("0")).Store.OK.Count;
string storeWithHighestCountOfOK = allStores.OrderBy(x => x.Store.OK.Count).Last().StoreID;
You can create separate methods for different sorting/queries you want to perform on each of the items for ease of getting the numbers you want.
Upvotes: 0
Reputation: 15294
I wanted to post an alternative solution where you can serialize the 0
index and any other indexes that follow it to achieve something like this.
The trick is to use a Dictionary
. If you expect that the ID
number will always be an integer
, then you can construct the first part of the dictionary like this to start with.
Dictionary<int, ...>
And if it's a string, just change the int
to a string
.
If VK
, OK
and WA
are the only 3 elements you expect, you can use the AllObjects
class from Jawads answer like this.
// Dictionary<int, AllObjects>>
JsonConvert.DeserializeObject<Dictionary<int, AllObjects>>(json);
I would also modify Jawads AllObjects
class to make sure the property names comply with C# conventions by using the JsonProperty
attributes to our advantage like this.
public class AllObjects
{
[JsonProperty("vk")]
public CostCountResponse Vk { get; set; }
[JsonProperty("ok")]
public CostCountResponse Ok { get; set; }
[JsonProperty("wa")]
public CostCountResponse Wa { get; set; }
}
The output of deserializing will give us this result.
If however you are expecting more elements than just VK
, OK
and WA
, you can cover this case with another nested dictionary like this.
Dictionary<int, Dictionary<string, ...>>
This string in the nest Dictionary is what will contain vk
, ok
, etc..
So what we have now is a Dictionary within a Dictionary which accurately represents how the JSON data is nested so far.
The final part is deserializing the JSON element containing the Cost
and Count
properties, and we can use the class Jawad posted to do that (I'm showing one that's again slightly modified to keep with naming conventions)
public class ObjInfo
{
[JsonProperty("cost")]
public int Cost { get; set; }
[JsonProperty("count")]
public int Count { get; set; }
}
We can now use the ObjInfo
class as the final puzzle of the Dictionary
we've been defining.
Dictionary<int, Dictionary<string, ObjInfo>>
Which we can use like this (I've included the modified JSON I've been using as well to demonstrate what we can do here)
static void Main()
{
var root = JsonConvert.DeserializeObject<Dictionary<int, Dictionary<string, ObjInfo>>>(testJson);
foreach (var item in root)
{
Console.WriteLine($"Id: {item.Key}");
foreach (var subitem in item.Value)
{
Console.WriteLine($" SubCode: {subitem.Key}");
Console.WriteLine($" Cost: {subitem.Value.Cost}");
Console.WriteLine($" Count: {subitem.Value.Count}\n");
}
}
// Or access individual items by
var zeroVk = root[0]["vk"];
// Console.WriteLine(zeroVk.Cost);
// Console.WriteLine(zeroVk.Count);
}
public class ObjInfo
{
[JsonProperty("cost")]
public int Cost { get; set; }
[JsonProperty("count")]
public int Count { get; set; }
}
const string testJson = @"{
""0"": {
""vk"": {
""cost"": 19,
""count"": 1903
},
""ok"": {
""cost"": 4,
""count"": 2863
},
""wa"": {
""cost"": 4,
""count"": 2210
}
},
""1"": {
""vk"": {
""cost"": 11,
""count"": 942
},
""ok"": {
""cost"": 68,
""count"": 1153
},
""wa"": {
""cost"": 14,
""count"": 7643
}
}
}";
This will spit out a response like this.
Id: 0
SubCode: vk
Cost: 19
Count: 1903
SubCode: ok
Cost: 4
Count: 2863
SubCode: wa
Cost: 4
Count: 2210
Id: 1
SubCode: vk
Cost: 11
Count: 942
SubCode: ok
Cost: 68
Count: 1153
SubCode: wa
Cost: 14
Count: 7643
Upvotes: 1
Reputation: 11364
One way to code it would be to Parse the input and Deserialize the "0" to a class.
var obj = JObject.Parse(jsonDoc);
var something = JsonConvert.DeserializeObject<AllObjects>(obj["0"].ToString());
and your classes would look like this (I know you can name them better :) )
public class ObjInfo
{
public int cost { get; set; }
public int count { get; set; }
}
public class AllObjects
{
public ObjInfo vk { get; set; }
public ObjInfo ok { get; set; }
public ObjInfo wa { get; set; }
}
Reason you might have to do the way i did above is because you cannot have a variable with number... like public AllObjects 0 {get;set;}
, but, you CAN do the following
public class MainObj
{
[JsonProperty("0")]
public AllObjects Zero { get; set; }
}
using the following line would deserialize correctly.
var something2 = JsonConvert.DeserializeObject<MainObj>(jsonDoc);
// where jsonDoc is your actual string input.
EDIT: If your initial json will have a random ID (not a 0), you can use the following code to look up that ID. Then you can query your objects to see which one needs updating.
var obj = JObject.Parse(jsonDoc);
var zeroethElement = ((JProperty)obj.Properties().First()).Name;
Upvotes: 2