Skip to content

Up & Running

David West edited this page Apr 7, 2017 · 8 revisions

< Home

SerialTreeNode

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.

Serialization/De-serialization

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);

Summary

The SerialTreeNode class is a great choice for simple, data-centric hierarchies where only basic query operations are needed.

Entity Tree Overview >

Clone this wiki locally