问题
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 objectJSON is aware of
boolean
properties and it would be better to deserialize them asboolean
and not asstring
.
回答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