Create a nested json object from c# model

依然范特西╮ 提交于 2021-02-11 12:31:28

问题


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?


回答1:


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.




回答2:


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.



来源:https://stackoverflow.com/questions/45459097/create-a-nested-json-object-from-c-sharp-model

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!