PrometheusIX
PrometheusIX

Reputation: 11

C# Sorting JSON response string

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

Answers (3)

Jawad
Jawad

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

Aydin
Aydin

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.

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>>

Upvotes: 1

Jawad
Jawad

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

Related Questions