0

This is my json:

[
   {
      "ChangeFlowsFromParent":"false",
      "ChangeFlowsToParent":"true",
      "StreamType":"mainine",
      "streamName":"ArgOS_2_0",
      "Parent":"none",
      "Compliance":"Released",
      "children":[
         {
            "ChangeFlowsFromParent":"true",
            "ChangeFlowsToParent":"true",
            "StreamType":"Release",
            "streamName":"ArgOS_2_0_DHAL1",
            "Parent":"ArgOS_2_0",
            "Compliance":"Released",
            "children":[
               {
                  "ChangeFlowsFromParent":"false",
                  "ChangeFlowsToParent":"true",
                  "StreamType":"Release",
                  "streamName":"ArgOS_child_DHAL2",
                  "Parent":"ArgOS_2_0_DHAL1",
                  "Compliance":"Released",
                  "children":[
                     {
                        "ChangeFlowsFromParent":"false",
                        "ChangeFlowsToParent":"true",
                        "StreamType":"Release",
                        "streamName":"ArgOS_child_Gen2",
                        "Parent":"ArgOS_child_DHAL2",
                        "Compliance":"Released"
                     }
                  ]
               }
            ]
         },
         {
            "ChangeFlowsFromParent":"true",
            "ChangeFlowsToParent":"true",
            "StreamType":"Release",
            "streamName":"ArgOS_2_0_DHAL2",
            "Parent":"ArgOS_2_0",
            "Compliance":"NA"
         },
         {
            "ChangeFlowsFromParent":"false",
            "ChangeFlowsToParent":"false",
            "StreamType":"Release",
            "streamName":"ArgOS_2_0_DHAL3",
            "Parent":"ArgOS_2_0",
            "Compliance":"NA"
         }
      ]
   }
]

and this is my Model

public class TreeModel
{
    public string StreamName { get; set; }
    public string ParentName { get; set; }
    public string StreamType { get; set; }
    public bool ChangeFlowsFromParent { get; set; }
    public bool ChangeFlowsToParent { get; set; }
    public string Compliance { get; set; }
    public string Parent { get; set; }

}

So I have a data in the form of my model and I need to create a nested Json structure like the one mentioned above. Based on the parent = streamname, a children tag will have to be created and that model item would be added as a json array.

This JSON is for my tree graph. How is this achievable?

3
  • Please format your json, it's unreadable as it is now. Commented Aug 2, 2017 at 11:09
  • Build a class that represents the JSON, fill the class with data and deserialize it. Commented Aug 2, 2017 at 11:10
  • Sorry is streamName the primaryKey and you want a single object for all the object with the same key? Commented Aug 2, 2017 at 11:23

2 Answers 2

3

Whenever you had to serialize data design a class structure that will exactly fit to the expected im-/export structure. Do not care for the rest of your application, just focus on im-/export.

The given JSON can be represented with

class TreeModelJson
{
    [JsonProperty("ChangeFlowsFromParent")]
    public string ChangeFlowsFromParent { get; set; }
    [JsonProperty("ChangeFlowsToParent")]
    public string ChangeFlowsToParent { get; set; }
    [JsonProperty("StreamType")]
    public string StreamType { get; set; }
    [JsonProperty("streamName")]
    public string StreamName { get; set; }
    [JsonProperty("Parent")]
    public string Parent { get; set; }
    [JsonProperty("Compliance")]
    public string Compliance { get; set; }
    [JsonProperty("children", NullValueHandling = NullValueHandling.Ignore)]
    public ICollection<TreeModelJson> Children { get; set; }
}

Now it is time to write a mapper from your application model to the JSON model

static ICollection<TreeModelJson> MapToTreeModelJsonCollection(ICollection<TreeModel> source)
{
    // map all items
    var allItems = source.Select(e => new TreeModelJson
    {
        ChangeFlowsFromParent = e.ChangeFlowsFromParent.ToString().ToLower(),
        ChangeFlowsToParent = e.ChangeFlowsToParent.ToString().ToLower(),
        Compliance = e.Compliance,
        Parent = e.Parent ?? "none",
        StreamName = e.StreamName,
        StreamType = e.StreamType,
    }).ToList();

    // build tree structure
    foreach (var item in allItems)
    {
        var children = allItems.Where(e => e.Parent == item.StreamName).ToList();
        if (children.Any())
        {
            item.Children = children;
        }
    }

    // return only root items
    return allItems.Where(e => e.Parent == "none").ToList();
}

Now it is time to bring it all together

var source = new List<TreeModel>
{
    ... // populate some data
};
var output = MapToTreeModelJsonCollection(source);
var json = JsonConvert.SerializeObject(output,Formatting.Indented);

Complete example on .net fiddle

Some notes

  • the Parent property in JSON is redundant, because this information is already given if the object is a children of another object

  • JSON is aware of boolean properties and it would be better to deserialize them as boolean and not as string.

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

Comments

0

There is a lot of room to optimize this but if it's not that big of an array no need to do micro optimizations.

/// <summary>
/// Creates a JObject from the tree node
/// </summary>
/// <param name="treeModel">The node to serialize</param>
/// <param name="context">All items</param>
/// <returns></returns>
public static JObject CreateJObject(TreeModel treeModel,IList<TreeModel> context) 
{
    JObject result = JObject.FromObject(treeModel);

    //This is not really needed but will cut the size of array for next iterations
    context.Remove(treeModel);

    //Used stream for the primary key.
    result["children"] = GetChildren(treeModel.StreamName, context);

    return result;
}

/// <summary>
/// Gets the children of the parent from context object
/// </summary>
/// <param name="id">id of the node</param>
/// <param name="context">All the nodes to read the children from</param>
/// <returns></returns>
public static JArray GetChildren(string id, IList<TreeModel> context) 
{
    //I used Parent for the forign key for the 
    return new JArray(context.Where(c => c.Parent == id).ToArray().Select(c => CreateJObject(c, context)));
} 

I tested it on something like this

 var items = new[] 
            {
                 new TreeModel{ StreamName = "a" },
                 new TreeModel{ StreamName = "b" , Parent = "a" },
                 new TreeModel{ StreamName = "c" , Parent = "a" },
                 new TreeModel{ StreamName = "d" , Parent = "b" }
            };

            //A list of all object I use a copy to remove already added items
        var context = items.ToList();

        //Gets the root elements the ones that have no parents
        var root = items.Where(tm => String.IsNullOrEmpty(tm.Parent) || tm.Parent == "none").Select(tm => CreateJObject(tm, context));    
            var data = new JArray(root);

            Console.WriteLine(data.ToString());

As I already mentioned there is room for optimization but this gets the job done.


NOTE Using the node removal in the program will also give you a solution for a different problem you may encounter, the problem of nodes that have a parent ID but the parent is not included. If you look at the context after serialization it will give you all the nodes that have a parent but the parent is not inside the original list in other words the nodes that are not serialized.

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.