0

I have a JSON that looks like this:

[  
   {  
      "type":"car",
      "types":[  
         {  
            "type":"ferrari",
            "types":[  
               {  
                  "type":"big",
                  "count":5
               },
               {  
                  "type":"small",
                  "count":1
               }
            ]
         },
         {  
            "type":"volvo",
            "types":[  
               {  
                  "type":"big",
                  "count":2
               }
            ]
         }
      ]
   },
   {  
      "type":"bike",
      "types":[  
         {  
            "type":"Ducati",
            "types":[  
               {  
                  "type":"small",
                  "count":1
               }
            ]
         }
      ]
   }
]

It's like a "group by" but nested. I want to convert it so that it is not nested. Something like this:

[  
   {  
      "types":[ "car", "ferrari", "big" ],
      "count":5
   },
   {  
      "types":[ "car", "ferrari", "small" ],
      "count":1
   },
   {  
      "types":[ "car", "volvo", "big" ],
      "count":2
   },
   {  
      "types":[ "bike", "ducati", "small" ],
      "count":1
   }
]

I got stuck because it's a recursive function, but complicated because I need to create a JObject for each combination. Actually more complicated because I don't know how nested the answer is. I know to stop when there is no types property. I'm trying to do this with JObjects.

4
  • Hi, perhaps continue recursing until you've extracted all the nested type values? Commented May 1, 2019 at 20:45
  • it should be helping you Generic method to convert a flat JSON array to nested JSON Commented May 1, 2019 at 20:47
  • 1
    @Faraz: That question is the opposite of this one. Commented May 1, 2019 at 20:47
  • 1
    @Pichi: What you posted isn't valid JSON. Commented May 1, 2019 at 20:50

2 Answers 2

4

Here's how I would approach this:

  1. Parse the JSON to a JArray, then use SelectTokens with the recursive descent operator .. to find all the count tokens. You will have one result object for each of these.

  2. Create a new JArray to hold the result objects.

  3. For each of the count tokens:

    a) Walk up the chain of ancestors and collect the type values from the JObject at each level into a new JArray. (You will need to reverse the order of these so they appear in the array from the top down instead of bottom up.)

    b) Assemble the count and the array of types into a new JObject and add that to the result array.

  4. Finally, convert the result JArray back to a JSON string.

Here is what it would look like in code:

var counts = JArray.Parse(json).SelectTokens("..count");
var array = new JArray();

foreach (var count in counts)
{
    var types = count.Ancestors()
                     .OfType<JObject>()
                     .Select(o => (string)o["type"])
                     .Reverse();

    var result = new JObject(
        new JProperty("types", new JArray(types)),
        new JProperty("count", count)
    );

    array.Add(result);
}

json = array.ToString();

Working demo here: https://dotnetfiddle.net/uI2Bzt

If you like terse code, you can do it all in "one line":

json = new JArray(
    JArray.Parse(json)
          .SelectTokens("..count")
          .Select(c =>
              new JObject(
                  new JProperty("types",
                      new JArray(
                          c.Ancestors()
                           .OfType<JObject>()
                           .Select(o => (string)o["type"])
                           .Reverse()
                      )
                  ),
                  new JProperty("count", c)
              )
          )
).ToString();

Fiddle: https://dotnetfiddle.net/jltZU5

Sign up to request clarification or add additional context in comments.

5 Comments

Wow... great... that helps! The problem is that inside volvo there could be [{“type”: “big”, “count”:2”},{“type”:”small”, “count”:3}] and from that create two arrays. All this is so group values are not repeated...
Meaning we would get another element in the array which is {“types”: [“car”, “volvo”, “small”], “count”: 3}
Please update your question to accurately describe what the possible JSON could be and what output you are trying to achieve depending on the input. You didn't mention anything about multiple counts before, and you gave only one example. It's still not clear to me what should happen in this case. Do you want an array of counts, with one for each type value? Or sum them?
Edited the example
OK, the picture is more clear now. I've updated my answer. Hopefully that is what you are looking for.
0

First, your json is invalid.

This is valid:

[
   {
      "type":"car",
      "types":[
         {
            "type":"volvo",
            "types":[
               {
                  "type":"big",
                  "count":2
               }
            ]
         }
      ]
   },
   {
      "type":"bike",
      "types":[
         {
            "type":"Ducati",
            "types":[
               {
                  "type":"small",
                  "count":1
               }
            ]
         }
      ]
   }
]

Use Json.NET to deserialize the object to this class:

public class MasterClass
{
    public TypesClass[] Property1 { get; set; }
}

public class TypesClass
{
    public string type { get; set; }
    public Type[] types { get; set; }
}

public class Type
{
    public string type { get; set; }
    public Types[] types { get; set; }
}

public class Types
{
    public string type { get; set; }
    public int count { get; set; }
}

Deserialize with:

DataSet dsSalesOrders = JsonConvert.DeserializeObject<DataSet>(response.Content);

Then with LINQ, you can use SelectMany on each subset to add them to your result.

3 Comments

It feels like too much classes... I feel it needs to be more an utils function maybe and not creating classes to change a JSON...
@PichiWuana No solution for lazyness
I don’t think it’s about lazyness, it’s more about being open to changes... (for example, it’s not types, it’s something else)

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.