1

I receive a bill of materials in JSON format via a WebApi, which has a corresponding hierarchy. The hierarchy or the nesting can be any depth.

An example bill of materials is shown below:

{
   "Quantity":0,
   "QuantityUnit":"pcs",
   "PartNumber":"12345",
   "Parent":"",
   "Children":[
      {
         "Quantity":1,
         "QuantityUnit":"pcs",
         "PartNumber":"88774",
         "Parent":"12345",
         "Children":[
            {
               "Quantity":1,
               "QuantityUnit":"pcs",
               "PartNumber":"42447",
               "Parent":"88774"
            },
            {
               "Quantity":0.420,
               "QuantityUnit":"kg",
               "PartNumber":"12387",
               "Parent":"88774"
            }
         ]
      }
   ]
}

How can I resolve this nested structure into a simple structure using JSON.NET in C#?

I want to transform it to:

[
   {
      "Quantity":0,
      "QuantityUnit":"pcs",
      "PartNumber":"12345",
      "Parent":""
   },
   {
      "Quantity":1,
      "QuantityUnit":"pcs",
      "PartNumber":"88774",
      "Parent":"12345"
   },
   {
      "Quantity":1,
      "QuantityUnit":"pcs",
      "PartNumber":"42447",
      "Parent":"88774"
   },
   {
      "Quantity":0.420,
      "QuantityUnit":"kg",
      "PartNumber":"12387",
      "Parent":"88774"
   }
]

For the deserialization I use the following class:

public class Bom
{
    public class TopLevel
    {
        public double Quantity { get; set; }
        public string QuantityUnit { get; set; }
        public string PartNumber { get; set; }
        public string Parent { get; set; }
        public List<Item> Children { get; set; }
    }

    public class Item
    {
        public double Quantity { get; set; }
        public string QuantityUnit { get; set; }
        public string PartNumber { get; set; }
        public string Parent { get; set; }
    }

    public double Quantity { get; set; }
    public string QuantityUnit { get; set; }
    public string PartNumber { get; set; }
    public string Parent { get; set; }
    public IList<TopLevel> Children { get; set; }
}

Furthermore, I use this code to deserialize the JSON to an object:

Bom bom = JsonConvert.DeserializeObject<Bom>(File.ReadAllText(jsonPath));

2 Answers 2

1

First let's define a mapper

JObject Map(JObject source)
{
    var result = (JObject)source.DeepClone();
    result.Remove("Children");
    return result;
}
  • It simply clones the object and removes the Children property

Next let's define a recursive function to accumulate the JObjects

void Flatten(JArray children, JArray accumulator)
{
    if (children == null) return;
    foreach (JObject child in children)
    {
        accumulator.Add(Map(child));
        Flatten((JArray)child["Children"], accumulator);
    }
}

And finally let's make use of them

var semiParsed = JObject.Parse(json);

var accumulator = new JArray();
accumulator.Add(Map(semiParsed));
Flatten((JArray)semiParsed["Children"], accumulator);

The ToString call on the accumulator will return this

[
  {
    "Quantity": 0,
    "QuantityUnit": "pcs",
    "PartNumber": "12345",
    "Parent": ""
  },
  {
    "Quantity": 1,
    "QuantityUnit": "pcs",
    "PartNumber": "88774",
    "Parent": "12345"
  },
  {
    "Quantity": 1,
    "QuantityUnit": "pcs",
    "PartNumber": "42447",
    "Parent": "88774"
  },
  {
    "Quantity": 0.42,
    "QuantityUnit": "kg",
    "PartNumber": "12387",
    "Parent": "88774"
  }
]

UPDATE #1

If your source json contains a deep hierarchy (lets say more than 5 levels) then the DeepClone is not really efficient, since you are copying the whole subtree.

To fix this problem you just need to rewrite the Map function

JObject Map(JObject source)
=> JObject.FromObject(new
{
    Quantity = (double)source["Quantity"],
    QuantityUnit = (string)source["QuantityUnit"],
    PartNumber = (string)source["PartNumber"],
    Parent = (string)source["Parent"]
});
Sign up to request clarification or add additional context in comments.

10 Comments

Great, it works perfectly! Thanks a lot! But I would not have come up with this solution in life. :)
@Mec-Eng I've just extended my post with some optimization suggestion.
Thank you so much! Indeed the json (bom) contains a deep hierarchy up to 12 (partly 15).
In your Update #1: What about Remove("Children")? After I implemented the new MAP-Function the keys are like Item1, Item1 and so on.
I replaced the whole Code with your example from dotnetfiddle and it works perfect. Probably a typo or something similar. Sorry for that ... Thank you so much! :)
|
0

Deserialize the original list, flatten it with Enumerable.SelectMany, and serialize the resulting sequence.

1 Comment

Thanks to your help I was able to deserialize the JSON and now I have the appropriate object. (compare class in edited post) But I don't understand how to use the Enumerable.SelectMany method here.

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.