0

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:

  • Call the ID to return all the blocks in that ID
  • Call ID.Object to get the cost and count
  • Call ID.Object.Count to return the account.

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.

3
  • 1
    Are you always getting vk, ok and wa as the objects? Commented Dec 15, 2019 at 22:32
  • I think deserializing to a Dictionary<string, Dictionary<string, Product>> would be easiest. See dotnetfiddle.net/dx2PVE which was going to be in answer to Handling JSON into readable data by Connor Raven but which got deleted. Commented Dec 16, 2019 at 21:01
  • @PrometheusIX added another answer to this question to help with filtering Commented Dec 16, 2019 at 21:47

3 Answers 3

2

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;
Sign up to request clarification or add additional context in comments.

6 Comments

You know what, I could take the extra work and just do this solution. The ObjInfo things will total about 150 but they are static so it'll work. Thank you.
@PrometheusIX see my edit.. for looking up ID (first element) in your json.
Only because you mention doing the extra work to create models. Here is a site I use to paste the JSON into and have the models created for me. They sometimes take tweaking but it gets you to the 90% of having models to deserialize to.
The thing is there is 0 to around 110 of these blocks, I can see how it works for the jsonproperty but I would need to define a unique string for each of them.
Yeah, think about which works best, whether creating the top MainObj based on zeroethElement or something else
|
1

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.

Dictionary<int, Dictionary<string, ObjInfo>>

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.
enter image description here


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

Dictionary<int, Dictionary<string, ObjInfo>>

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.