-
Notifications
You must be signed in to change notification settings - Fork 25
Up & Running
Let’s start with the SerialTreeNode class. This class is good for expressing hierarchical structures in the simplest form possible, most notably those that can be created in a fluent manner with code. As we’ll see below, a SerialTreeNode tree can be created in a single, nested constructor. As the name suggests, it can be easily serialized/de-serialized and may be an ideal candidate for the basis of a data model in a non-relational data store and configuration scenarios. Later on we’ll see how trees of this type can easily be mapped to trees with more interesting capabilities.
SerialTreeNode is an abstract class, so we must first create a concrete implementation. In the example that follows, note the recursive type parameter.
public class CategoryDataNode : SerialTreeNode<CategoryDataNode>
{
// empty constructor is a type constraint imposed by the base class
public CategoryDataNode() { }
public CategoryDataNode(long categoryId,
string name,
params CategoryDataNode[] children)
: base(children)
{
CategoryId = categoryId;
Name = name;
}
public long CategoryId { get; set; }
public string Name { get; set; }
}
Now let’s create a composite tree with this type:
var root =
new CategoryDataNode(1, "Root",
new CategoryDataNode(2, "Nouns",
new CategoryDataNode(3, "People",
new CategoryDataNode(4, "Celebrities"),
new CategoryDataNode(5, "Bad Guys")),
new CategoryDataNode(6, "Places",
new CategoryDataNode(13, "Local"),
new CategoryDataNode(14, "National"),
new CategoryDataNode(15, "International")),
new CategoryDataNode(7, "Things",
new CategoryDataNode(16, "Concrete",
new CategoryDataNode(21, "Everyday Objects"),
new CategoryDataNode(22, "Tools")),
new CategoryDataNode(17, "Abstract",
new CategoryDataNode(4, "Philosophies"),
new CategoryDataNode(19, "Math")))),
new CategoryDataNode(8, "Verbs",
new CategoryDataNode(10, "Active"),
new CategoryDataNode(11, "Passive")),
new CategoryDataNode(9, "Adjectives",
new CategoryDataNode(12, "Sensory",
new CategoryDataNode(23, "Colors"),
new CategoryDataNode(1, "Smells")),
new CategoryDataNode(20, "Sizes")));
The following code snippets illustrate SerialTreeNode operations:
IEnumerable<CategoryDataNode> allNodes = root.SelectAll();
IEnumerable<CategoryDataNode> sCategories = root.Where(n => n.Name.StartsWith("S"));
CategoryDataNode thingsRoot = root.FirstOrDefault(n => n.Name == "Things");
root.ForEach((n,i) => Console.WriteLine($"{n.Name} level: {i}"));"
IEnumerable<CategoryDataNode> things =
root
.FirstOrDefault(n => n.Name == "Things")
.SelectAll();
// formatted output
Console.WriteLine(tree.ToString(n => $"{n.CategoryId} {n.Name}"));
Note the similarity to standard LINQ operators in several of the examples. This is just coincidental; SerialTreeNode does not actually expose an enumerator. However, all other node types in the TreeCollections library do expose an enumerator and are fully LINQ-capable.
Trees made using the SerialTreeNode class can be easily serialized and de-serialized.
For example, JSON conversions are straightforward using the popular Newtonsoft JSON library:
string json = JsonConvert.SerializeObject(root);
CategoryDataNode deserialized JsonConvert.DeserializeObject<CategoryDataNode>(json);
The SerialTreeNode class is a great choice for simple, data-centric hierarchies where only basic query operations are needed.