Adding the Parent id to Serialization as Object class - c#

I have the following XML file which I am using the VSC#(windows forms) code to save it as a class:
<Steps >
<Step id ="1" Name="S1">
<Step id ="2" Name="S11">
<Step id ="3" Name="S111" />
<Step id ="4" Name="S112" />
<Step id ="5" Name="S1121" />
</Step >
<Step id ="6" Name="S12" />
</Step >
</Steps >
The code I wrote as:
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class Steps
{
[System.Xml.Serialization.XmlElementAttribute("Step")]
public List<Step> Step { get; set; }
}
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class Step
{
[System.Xml.Serialization.XmlElementAttribute("Step")]
public List<Step> Step1 { get; set; }
[System.Xml.Serialization.XmlAttributeAttribute()]
public string name { get; set; }
[System.Xml.Serialization.XmlAttributeAttribute()]
public string id { get; set; }
[System.Xml.Serialization.XmlAttributeAttribute()]
public string ParentID { get; set; }
}
I have two questions:
How can I get the ParentID into the child field for
children?(there would be only null for node with id=1, otherwise
each child has its parents id)
The second question is that after coding in object class, how could
I insert a desired child with giving the id name? For example, I
would like to insert a child with id=4C and name=S112C after
node with id=4?
Update:(after answering both question)
Let's we assume that I want to create a new field as Hierarchy in the Step which takes values of string created/given by user
Step.Hierarchy = // some strings ;
It means I want to replace it with ParentId. The reason is that because sometimes there are some situations which I should insert two empty nodes/components(There is no name and Id for it, as below) as a child for some steps
steps.Add(new Step { Id = " ", Name = " " }, "4");
where one empty node will be child of other one. Then I will have difficulty for giving PrentId reference for the second node(child to the above node).
steps.Add(new Step { Id = " ", Name = " " }, " ");
This is why I want to create a virtual field like Hierarchy to assign an arbitrary value to it and refer ParentId to it instead of Id. Then each Step has a non null reference.
If you have an idea that would be thankful!!

How can I ensure that child.ParentId always equals parent.Id after deserializing?
The natural approach to setting Step.ParentId after deserialization would be to do so in an OnDeserialized event. Unfortunately, XmlSerializer does not support deserialization events. Given that, you may need to investigate an alternate design.
One possibility is to replace your List<Step> with a custom collection that automatically maintains the ParentId reference when a child is added to a parent, along the lines of Maintaining xml hierarchy (ie parent-child) information in objects generated by XmlSerializer. Unfortunately, ObservableCollection is not suitable for this purpose, because the list of old items is not included in the notification event when it is cleared. However, it's quite easy to make our own by subclassing System.Collections.ObjectModel.Collection<T>.
Thus, your object model would become the following. Note that I have modified some of your property names to follow c# naming guidelines:
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class Steps
{
readonly ChildCollection<Step> steps;
public Steps()
{
this.steps = new ChildCollection<Step>();
this.steps.ChildAdded += (s, e) =>
{
if (e.Item != null)
e.Item.ParentId = null;
};
}
[System.Xml.Serialization.XmlElementAttribute("Step")]
public Collection<Step> StepList { get { return steps; } }
}
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class Step
{
readonly ChildCollection<Step> steps;
public Step()
{
this.steps = new ChildCollection<Step>();
this.steps.ChildAdded += (s, e) =>
{
if (e.Item != null)
e.Item.ParentId = this.Id;
};
}
[System.Xml.Serialization.XmlElementAttribute("Step")]
public Collection<Step> StepList { get { return steps; } }
[System.Xml.Serialization.XmlAttributeAttribute("Name")]
public string Name { get; set; }
[System.Xml.Serialization.XmlAttributeAttribute("id")]
public string Id { get; set; }
[System.Xml.Serialization.XmlAttributeAttribute("ParentID")]
public string ParentId { get; set; }
}
public class ChildCollectionEventArgs<TChild> : EventArgs
{
public readonly TChild Item;
public ChildCollectionEventArgs(TChild item)
{
this.Item = item;
}
}
public class ChildCollection<TChild> : Collection<TChild>
{
public event EventHandler<ChildCollectionEventArgs<TChild>> ChildAdded;
public event EventHandler<ChildCollectionEventArgs<TChild>> ChildRemoved;
void OnRemoved(TChild item)
{
var removed = ChildRemoved;
if (removed != null)
removed(this, new ChildCollectionEventArgs<TChild>(item));
}
void OnAdded(TChild item)
{
var added = ChildAdded;
if (added != null)
added(this, new ChildCollectionEventArgs<TChild>(item));
}
public ChildCollection() : base() { }
protected override void ClearItems()
{
foreach (var item in this)
OnRemoved(item);
base.ClearItems();
}
protected override void InsertItem(int index, TChild item)
{
OnAdded(item);
base.InsertItem(index, item);
}
protected override void RemoveItem(int index)
{
if (index >= 0 && index < Count)
{
OnRemoved(this[index]);
}
base.RemoveItem(index);
}
protected override void SetItem(int index, TChild item)
{
OnAdded(item);
base.SetItem(index, item);
}
}
Now ParentId will be set whenever a child is added to a parent, both after deserialzation, and in any applications code.
(If for whatever reason you cannot replace your List<Step> with a Collection<Step>, you could consider serializing an array proxy property and setting the ParentId values in the setter, along the lines of XML deserialization with parent object reference. But I think a design that automatically sets the parent id in all situations is preferable.)
How can I add a Step to a tree of Step objects by specifying ParentId?
You could create recursive Linq extensions that traverse the Step hierarchy, along the lines of Efficient graph traversal with LINQ - eliminating recursion:
public static class StepExtensions
{
public static IEnumerable<Step> TraverseSteps(this Steps root)
{
if (root == null)
throw new ArgumentNullException();
return RecursiveEnumerableExtensions.Traverse(root.StepList, s => s.StepList);
}
public static IEnumerable<Step> TraverseSteps(this Step root)
{
if (root == null)
throw new ArgumentNullException();
return RecursiveEnumerableExtensions.Traverse(root, s => s.StepList);
}
public static bool TryAdd(this Steps root, Step step, string parentId)
{
foreach (var item in root.TraverseSteps())
if (item != null && item.Id == parentId)
{
item.StepList.Add(step);
return true;
}
return false;
}
public static void Add(this Steps root, Step step, string parentId)
{
if (!root.TryAdd(step, parentId))
throw new InvalidOperationException(string.Format("Parent {0} not found", parentId));
}
}
public static class RecursiveEnumerableExtensions
{
// Rewritten from the answer by Eric Lippert https://stackoverflow.com/users/88656/eric-lippert
// to "Efficient graph traversal with LINQ - eliminating recursion" http://stackoverflow.com/questions/10253161/efficient-graph-traversal-with-linq-eliminating-recursion
// to ensure items are returned in the order they are encountered.
public static IEnumerable<T> Traverse<T>(
T root,
Func<T, IEnumerable<T>> children)
{
yield return root;
var stack = new Stack<IEnumerator<T>>();
try
{
stack.Push((children(root) ?? Enumerable.Empty<T>()).GetEnumerator());
while (stack.Count != 0)
{
var enumerator = stack.Peek();
if (!enumerator.MoveNext())
{
stack.Pop();
enumerator.Dispose();
}
else
{
yield return enumerator.Current;
stack.Push((children(enumerator.Current) ?? Enumerable.Empty<T>()).GetEnumerator());
}
}
}
finally
{
foreach (var enumerator in stack)
enumerator.Dispose();
}
}
public static IEnumerable<T> Traverse<T>(
IEnumerable<T> roots,
Func<T, IEnumerable<T>> children)
{
return from root in roots
from item in Traverse(root, children)
select item;
}
}
Them to add a child to a specific parent by ID, you would do:
steps.Add(new Step { Id = "4C", Name = "S112C" }, "4");
Prototype fiddle.
Update
If you somehow are having trouble adding extension methods to Step and Steps because they are nested classes, you could add TraverseSteps() and Add() as object methods:
public partial class Step
{
public IEnumerable<Step> TraverseSteps()
{
return RecursiveEnumerableExtensions.Traverse(this, s => s.StepList);
}
}
public partial class Steps
{
public IEnumerable<Step> TraverseSteps()
{
return RecursiveEnumerableExtensions.Traverse(StepList, s => s.StepList);
}
public bool TryAdd(Step step, string parentId)
{
foreach (var item in TraverseSteps())
if (item != null && item.Id == parentId)
{
item.StepList.Add(step);
return true;
}
return false;
}
public void Add(Step step, string parentId)
{
if (!TryAdd(step, parentId))
throw new InvalidOperationException(string.Format("Parent {0} not found", parentId));
}
}

Related

Iterate over a json input and create the treeview like hierarchy based on the "keys" taking account of the children "keys"

I have the following piece of code:
JObject my_obj = JsonConvert.DeserializeObject<JObject>(ReceivedJson);
ParseJson(my_obj); //method to store all the nested "keys" and the "id" values
public void ParseJson(JObject obj)
{
foreach (KeyValuePair<string, JToken> sub_obj in (JObject)obj["Soccer"])
{
Console.WriteLine(sub_obj.Key);
}
}
//this does not work well as I cant access all the nested keys :/
I am receiving a json in the following format. It could be nested on several levels and I want to be able to store the nested "keys" and their respective "T_id" value in a dictionary.
The json is as follows:
{
"Soccer":{
"T_id":0,
"T_state":"valid",
"Clubs":{
"ClubA":{
"T_id":"1",
"T_state":"Champs"
},
"ClubB":{
"T_id":"2",
"T_state":"Runnerups"
}
},
"Subs":{
"SubA":{
"T_id":"3",
"T_state":"Unfit",
//this is nested key
"SubE":{
"T_id":"3",
"T_state":"Unfit"
}
}
},
"Subs_Used":{
"SubK":{
"T_id":"3",
"T_state":"Unfit"
}
}
//many more nested n-levels
}
}
I want to be able to extract the "keys" and create a nested structure like this:
>Soccer
>Clubs
ClubA
ClubB
>Subs
SubA
>Subs_Used
SubK
where each node has two fields, { string key, int T_id }
The "keys" could be nested deeply and I want to have a generic method which allows me to create this hierarchy while iterating over the JObject.
Is there a simple approach to do this? I am really lost and would appreciate help to make progress.
What you want to do is to map your deeply nested JSON into a c# tree where each node has two properties -- a string key and a long T_id -- as well as a collection of children of the same type.
You could model this as follows, using a list:
public partial class KeyIdObject
{
public string key { get; set; }
public long T_id { get; set; }
public List<KeyIdObject> Children { get; set; }
}
Once you have you data model, you need to use a recursive algorithm to generate your nodes. A related algorithms is shown in Searching for a specific JToken by name in a JObject hierarchy, but you need a two-stage recursion:
Descend through the JToken hierarchy until you find a JObject with a T_id property.
Once you have found a match, construct a KeyIdObject for it and populate its list of children by searching the matching JObject's children using a nested recursive search.
Then move on to the matches's next sibling in the outer recursive search.
This can be accomplished by introducing an extension method that searches for the topmost descendants of a given JToken that match a given condition:
public static partial class JsonExtensions
{
/// <summary>
/// Enumerates through all descendants of the given element, returning the topmost elements that match the given predicate
/// </summary>
/// <param name="root"></param>
/// <param name="filter"></param>
/// <returns></returns>
public static IEnumerable<TJToken> TopDescendantsWhere<TJToken>(this JToken root, Func<TJToken, bool> predicate) where TJToken : JToken
{
if (predicate == null)
throw new ArgumentNullException();
return GetTopDescendantsWhere<TJToken>(root, predicate, false);
}
static IEnumerable<TJToken> GetTopDescendantsWhere<TJToken>(JToken root, Func<TJToken, bool> predicate, bool includeSelf) where TJToken : JToken
{
if (root == null)
yield break;
if (includeSelf)
{
var currentOfType = root as TJToken;
if (currentOfType != null && predicate(currentOfType))
{
yield return currentOfType;
yield break;
}
}
var rootContainer = root as JContainer;
if (rootContainer == null)
yield break;
var current = root.First;
while (current != null)
{
var currentOfType = current as TJToken;
var isMatch = currentOfType != null && predicate(currentOfType);
if (isMatch)
yield return currentOfType;
// If a match, skip children, but if not, advance to the first child of the current element.
var next = (isMatch ? null : current.FirstChild());
if (next == null)
// If no first child, get the next sibling of the current element.
next = current.Next;
// If no more siblings, crawl up the list of parents until hitting the root, getting the next sibling of the lowest parent that has more siblings.
if (next == null)
{
for (var parent = current.Parent; parent != null && parent != root && next == null; parent = parent.Parent)
{
next = parent.Next;
}
}
current = next;
}
}
static JToken FirstChild(this JToken token)
{
var container = token as JContainer;
return container == null ? null : container.First;
}
}
Then, you can use it to generate a recursive List<KeyIdObject> like so:
public partial class KeyIdObject
{
public static List<KeyIdObject> ToIdObjects(JToken root)
{
return root.TopDescendantsWhere<JObject>(o => o["T_id"] != null)
.Select(o => new KeyIdObject { key = ((JProperty)o.Parent).Name, T_id = (long)o["T_id"], Children = ToIdObjects(o) })
.ToList();
}
}
Demo fiddle #1 here, which generates the following structure:
[
{
"key": "Soccer",
"T_id": 0,
"Children": [
{
"key": "ClubA",
"T_id": 1
},
{
"key": "ClubB",
"T_id": 2
},
{
"key": "SubA",
"T_id": 3,
"Children": [
{
"key": "SubE",
"T_id": 3
}
]
},
{
"key": "SubK",
"T_id": 3
}
]
}
]
However, in your JSON some of your object node(s), specifically "Clubs" and "Subs", do not have a T_id property. Thus, they can't be captured into the node hierarchy as there is no way to populate the long T_id value. If you do need to capture these nodes, you can modify your data model to have a nullable value for the id and capture the intermediate nodes as follows:
public partial class KeyIdObject
{
public string key { get; set; }
public long? T_id { get; set; }
public List<KeyIdObject> Children { get; set; }
}
public partial class KeyIdObject
{
public static List<KeyIdObject> ToIdObjects(JToken root)
{
return root.TopDescendantsWhere<JObject>(o => true)
.Select(o => new KeyIdObject { key = ((JProperty)o.Parent).Name, T_id = (long?)o["T_id"], Children = ToIdObjects(o) })
.ToList();
}
}
Demo fiddle #2 here.
Finally, if you are sure your keys are unique at any given level, you could use a dictionary instead of a list, like so:
public partial class IdObject
{
public long T_id { get; set; }
public Dictionary<string, IdObject> Children { get; set; }
}
public partial class IdObject
{
public static Dictionary<string, IdObject> ToIdObjects(JToken root)
{
return root.TopDescendantsWhere<JObject>(o => o["T_id"] != null)
.ToDictionary(o => ((JProperty)o.Parent).Name,
o => new IdObject { T_id = (long)o["T_id"], Children = ToIdObjects(o) });
}
}
Demo fiddle #3 here.
Note that, in all cases, I chose long instead of int for the T_id for safety.
Update
If you are going to bind this into a WPF TreeView or something similar like a Syncfusion.Xamarin.SfTreeView, you will want to implement INotifyPropertyChanged and use ObservableCollection<T>. You may also want to use a different ItemTemplate for nodes with and without T_id values, in which case you can define a different c# POCO for each case. The following is one example:
public abstract partial class KeyItemBase : INotifyPropertyChanged
{
public KeyItemBase() : this(null, Enumerable.Empty<KeyItemBase>()) { }
public KeyItemBase(string key, IEnumerable<KeyItemBase> children)
{
this.m_key = key;
this.m_children = new ObservableCollection<KeyItemBase>(children);
}
string m_key;
public string key
{
get { return m_key; }
set
{
m_key = value;
RaisedOnPropertyChanged("key");
}
}
ObservableCollection<KeyItemBase> m_children;
public ObservableCollection<KeyItemBase> Children { get { return m_children; } }
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisedOnPropertyChanged(string _PropertyName)
{
var changed = PropertyChanged;
if (changed != null)
{
changed(this, new PropertyChangedEventArgs(_PropertyName));
}
}
}
public abstract partial class KeyItemBase
{
// Generate clean JSON on re-serialization.
public bool ShouldSerializeChildren() { return Children != null && Children.Count > 0; }
}
public sealed class KeyItem : KeyItemBase
{
// Use for a JSON object with no T_id property.
// Bind an appropriate SfTreeView.ItemTemplate to this type.
public KeyItem() : base() { }
public KeyItem(string key, IEnumerable<KeyItemBase> children) : base(key, children) { }
}
public class KeyIdItem : KeyItemBase
{
// Use for a JSON object with a T_id property.
// Bind an appropriate SfTreeView.ItemTemplate to this type.
public KeyIdItem() : base() { }
public KeyIdItem(string key, IEnumerable<KeyItemBase> children, long t_id) : base(key, children) { this.m_id = t_id; }
long m_id;
public long T_id
{
get { return m_id; }
set
{
m_id = value;
RaisedOnPropertyChanged("T_id");
}
}
}
public static class KeyItemFactory
{
public static KeyItemBase ToKeyObject(string name, long? id, IEnumerable<KeyItemBase> children)
{
if (id == null)
return new KeyItem(name, children);
else
return new KeyIdItem(name, children, id.Value);
}
public static IEnumerable<KeyItemBase> ToKeyObjects(JToken root)
{
return root.TopDescendantsWhere<JObject>(o => true)
.Select(o => ToKeyObject(((JProperty)o.Parent).Name, (long?)o["T_id"], ToKeyObjects(o)));
}
}
Which you would use as follows:
var items = new ObservableCollection<KeyItemBase>(KeyItemFactory.ToKeyObjects(root));
// Now bind items to your ItemsSource
// https://help.syncfusion.com/cr/cref_files/xamarin/Syncfusion.SfTreeView.XForms~Syncfusion.XForms.TreeView.SfTreeView~ItemsSource.html
Demo fiddle #4 here.

SpecimenBuilder for a collection

I have a need for customizing creation of a collection, with quite complicated relationships between the objects within it, and I can't figure out how to do it correctly.
For the sake of this issue, let's assume I'm working on a todo app. It has Items and SubItems, and the items have a week number indicating when they should be done:
public class Item {
public string Name { get; set; }
public int Week { get; set; }
public ICollection<SubItem> SubItems { get; set; }
}
public class SubItem {
public string Name { get; set; }
public Item Parent { get; set; }
}
Now, because this is what data usually looks like in the actual application, I want to create a collection of Items that has the following properties:
There are items that have the same name, but different weeks
There are items that have the same week but different name
There are sub-items that have the same name, but different parents
In order to do this, I've created a TodoItemSpecimenBuilder : ISpecimenBuilder which starts its Create method like this:
var type = (request as PropertyInfo)?.PropertyType ?? request as Type;
if (type == null || !typeof(IEnumerable<Item>).IsAssignableFrom(type))
{
return new NoSpecimen();
}
// build up the actual collection
return BuildActualCollection();
However, when I run tests with this specimen builder included in my context, I get lots (maybe 20 or 30) hits on the return statement before I enter even my setup code, and the first time I try to actually CreateMany<Item>(), it blows up with a cast exception because it can't cast OmitSpecimen to Item.
What am I doing wrong here?
Full sample code, compilable after installing NUnit and AutoFixture:
public class TodoList
{
public ICollection<Item> Tasks { get; set; }
}
public class Item
{
public string Name { get; set; }
public Week Week { get; set; }
public ICollection<SubItem> SubItems { get; set; }
public int ItemId { get; set; }
public TodoList TodoList { get; set; }
}
public class SubItem
{
public Item Item { get; set; }
public string Name { get; set; }
public int SortOrder { get; set; }
public string HelpText { get; set; }
}
public class Week
{
public int WeekId { get; set; }
}
public class ItemCollectionSpecimenBuilder : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
if (!IsApplicable(request))
{
return new NoSpecimen();
}
var items = new List<Item>(3);
var week1 = context.Create<Week>();
var week2 = context.Create<Week>();
items.Add(CreateItem(context, week1));
items.Add(CreateItem(context, week1));
items.Add(CreateItem(context, week2));
items.GroupBy(t => t.Week).ToList().ForEach(ConfigureNames);
ConfigureSubItems(context, items);
return items;
}
private static bool IsApplicable(object request)
{
bool IsManyItemsType(Type type) => typeof(IEnumerable<Item>).IsAssignableFrom(type);
bool IsItemsType(Type type) => type != null && typeof(Item) == type;
switch (request)
{
case PropertyInfo pInfo:
return IsManyItemsType(pInfo.PropertyType);
case Type type:
return IsManyItemsType(type);
case MultipleRequest multipleRequest:
if (!(multipleRequest.Request is SeededRequest seededRequest))
{
return false;
}
return IsItemsType(seededRequest.Request as Type);
default:
return false;
}
}
private static Item CreateItem(ISpecimenContext context, Week week)
{
var item = context.Create<Item>();
item.Week = week;
return item;
}
private static void ConfigureNames(IEnumerable<Item> items)
{
string name = null;
foreach (var item in items)
{
if (name == null)
{
name = item.Name;
}
else
{
item.Name = name;
}
}
}
private static void ConfigureSubItems(ISpecimenContext context, IEnumerable<Item> items)
{
foreach (var group in items.GroupBy(item => item.Week.WeekId))
{
var subItemTemplates = context.CreateMany<SubItem>().ToList();
foreach (var item in group)
{
item.SubItems.Clear();
foreach (var subItem in context.CreateMany<SubItem>().Zip(subItemTemplates,
(model, subItem) =>
{
subItem.Item = item;
subItem.Name = model.Name;
subItem.SortOrder = model.SortOrder;
subItem.HelpText = model.HelpText;
return subItem;
}))
{
item.SubItems.Add(subItem);
}
}
}
}
}
[TestFixture]
public class AutoFixtureSpecimenBuilderTests
{
private static void TestCreationOfTasks(Func<IFixture, ICollection<Item>> creator)
{
var fixture = new Fixture();
fixture.Customizations.Add(new ItemCollectionSpecimenBuilder());
fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList()
.ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior());
var tasks = creator(fixture);
Assert.AreEqual(3, tasks.Count);
Assert.AreEqual(2, tasks.GroupBy(t => t.Week).Count());
Assert.IsTrue(tasks.GroupBy(t => t.Week).Select(g => g.Select(t => t.Name).Distinct()).All(distinctNames => distinctNames.Count() == 1));
var task = tasks.GroupBy(t => t.Week).OrderBy(g => g.Count()).First().OrderBy(t => t.ItemId).First();
}
[Test]
public void CreateMany() => TestCreationOfTasks(fixture => fixture.CreateMany<Item>().ToList());
[Test]
public void CreateWithProperty() => TestCreationOfTasks(fixture => fixture.Create<TodoList>().Tasks);
[Test]
public void CreateAsList() => TestCreationOfTasks(fixture => fixture.Create<IList<Item>>());
}
I can't think of any particularly good way to address this issue. The problem is that Item is a recursive (tree-like) data structure, and while AutoFixture does have some support for such, it's not easily extensible.
When you create an ISpecimenBuilder, you tell AutoFixture that this object is going to handle requests for particular objects. This means that you can no longer use the context to request those objects, because that'll recurse back into the same builder, causing an infinite recursion.
So, one option is to build up the objects 'by hand' from within the builder. You can still request all other types, but you'll have to avoid requesting objects that cause recursion.
Another option is to add a post-processor. Here's a proof of concept:
public class ItemCollectionSpecimenCommand : ISpecimenCommand
{
public void Execute(object specimen, ISpecimenContext context)
{
var #is = specimen as IEnumerable<Item>;
if (#is == null)
return;
var items = #is.ToList();
if (items.Count < 3)
return;
var week1 = context.Create<Week>();
var week2 = context.Create<Week>();
items[0].Week = week1;
items[1].Week = week1;
items[2].Week = week2;
items.GroupBy(t => t.Week).ToList().ForEach(ConfigureNames);
}
private static void ConfigureNames(IEnumerable<Item> items)
{
string name = null;
foreach (var item in items)
{
if (name == null)
name = item.Name;
else
item.Name = name;
}
}
}
You can configure your fixture like this:
var fixture = new Fixture();
fixture.Customizations.Add(
SpecimenBuilderNodeFactory.CreateTypedNode(
typeof(IEnumerable<Item>),
new Postprocessor(
new EnumerableRelay(),
new CompositeSpecimenCommand(
new AutoPropertiesCommand(),
new ItemCollectionSpecimenCommand()))));
fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList()
.ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior());
This'll pass the repro tests CreateWithProperty and CreateAsList, but not CreateMany.
For various (historical) reasons, the way that CreateMany works is quite different from the way that something like Create<IList<>> works. If you really need this to work for CreateMany as well, I'll see what I can do, but I can't promise that this'll be possible at all.
After having looked at this repro for a few hours, this is the best I can come up with. I haven't really used AutoFixture for a year or two now, so it's possible that I'm simply out of shape, and that a better solution is available... I just can't think of it...

Composite pattern with generic leafs

In my C# .Net 4.0 composite pattern I want to have leafs that are generic. Most examples I found have a generic in the base node which propagates through the whole composite tree. I do not want that.
I have found the following solution (which I've stripped a bit to the essentials). An interface called INode which has two implementations. One called category which basically is a dictionary of INodes. It is a dictionary because I do not want duplicate leafs. The other implementation called ValueNode holds the information.
This allows for differently typed leaf nodes.
public interface INode
{
string Name { get; }
}
public class CategoryNode : INode
{
public CategoryNode(string name)
{
this.Name = name;
this.Children = new Dictionary<string, INode>();
}
public string Name { get; private set; }
public List<string> Keys
{
get { return this.Children.Keys.ToList(); }
}
private Dictionary<string, INode> Children { get; set; }
public INode this[string key]
{
get { return this.Children[key]; }
}
public void Add(INode node)
{
this.Children.Add(node.Name, node);
}
}
public class ValueNode<T> : INode
{
public ValueNode(
string name,
T defaultValue)
{
this.Name = name;
this.Value = this.Default = defaultValue;
}
public ValueNode(
string name,
T defaultValue)
{
this.Name = name;
this.Value = this.Default = defaultValue;
}
public T Default { get; private set; }
public T Value { get; set; }
public string Name { get; private set; }
}
Notice that I've made the children list private so nobody can remove nodes.
I am comfortable with this solution. However, the usage syntax it produces is a bit talkative. For example:
((this.root["category"] as CategoryNode)["leaf"] as ValueNode<int>).Value = (node as ValueNode<int>).Value;
While I had envisioned something like
this.root["category"]["leaf"] = node;
Does anybody have ideas for me to simplify the syntax?
How about adding an extension method to INode type ?
public static class INodeExtensions
{
public static void SetValue<T>(this INode node, string key, T v)
{
if(v is INode)
{
// category node set value
if(node is CategoryNode)
{
// convert and set value
}
else
{
throw new Exception("No children found.");
}
}
else
{
// value node set value
}
}
}
What about using a parameter array to specify the "path" to your leaf?
Optionally, there is another method in case you need to get a category node.
class CategoryNode : INode
{
public CategoryNode GetCategoryNode(params string[] path) {
CategoryNode cat = (CategoryNode)this.Children[path[0]];
for (int i = 1; i < path.Length; ++i) {
cat = (CategoryNode)cat.Children[path[i]];
}
return cat;
}
public ValueNode<T> GetLeafNode<T>(params string[] path) {
INode first = this.Children[path[0]];
if (path.Length == 1 && first is ValueNode<T>) return (ValueNode<T>)first;
CategoryNode cat = (CategoryNode)first;
for (int i = 1; i < path.Length - 1; ++i) {
cat = (CategoryNode)cat.Children[path[i]];
}
return (ValueNode<T>)cat.Children[path[path.Length-1]];
}
}
You use it like this:
var leafNode = root.GetLeafNode<int>("cat1", "cat2", "leaf");
// or
root.GetLeafNode<int>("cat1", "cat2", "leaf").Value = 1234;
The indexer is no longer needed.
I ended up with what Teddy proposed and also added a GetValue.
In addition, I put the indexer in the INode interface and just throw an exception when it is called on a value node. This way you can also use the this.root["category"]["leaf"] syntax.
You still must cast to a ValueNode<> if you want to access the value property though. But you can do this.root["category1"]["category2"].SetValue<int>("leaf", 42).

Search through HierarchicalData with recursion

I am building a treeview with a list of ScanItem. The class of ScanItem is actually:
public class ScanItem
{
public string FullPath { get; set; }
public string Name
{
get
{
return Path.GetFileName(FullPath);
}
}
public DateTime ModifiedDate { get; set; }
public DateTime CreatedDate { get; set; }
public FileAttributes Attributes { get; set; }
public bool IsDirectory { get; set; }
public string Extension
{
get
{
if (IsDirectory)
return "Folder";
else
return Path.GetExtension(Name);
}
}
public UInt64 Size { get; set; }
}
In order for me to create a treeview I needed to create two other classes in order to distinguish the folders and files in my treeview:
public class ScanFile : ScanItem
{
}
public class ScanDir : ScanItem
{
public List<ScanItem> Items { get; set; }
public ScanDir()
{
Items = new List<ScanItem>();
}
}
Note that the class ScanFile is just like the ScanItem and the ScanDir class has an extra property called Items and will contain a list of items of itself.
So if I where to iterate through this direcotory (C:\Temp):
my List will actually contain:
note that if I expand one ScanDir object I will get another List:
in order to populate the following treeview:
So I was able to populate this list using recursion by searching for files and directories in a specific path.
I just wanted to explain my situation because there are several places in the internet that enable you to filter a treeview and that is what I actually want to do. But it will be nice if I can iterate through each item in List and then remove it if some criteria is not met:
I have actually tried using the following recursive method to filter my results.
public List<ScanItem> search(List<ScanItem> items)
{
var filter = new List<ScanItem>();
foreach (var item in items)
{
if (!item.FullPath.Contains("stringIwantToLookFor")) continue;
filter.Add(item);
if (item.IsDirectory)
{
search(((ScanDir)item).Items);
}
}
return filter;
}
I think that if an item is found I need to add all the parent root directories and that's why it does not work. The reason why I want to build my own recursion method is because I want to be able to filter the treeview based on spesific criteria.
EDIT:
In other words if I want to have all the items that contain "X.txt" in my listview I want to just see:
I would do it like this: create public abstract ScanItem Seach(string s) on your ScanItem. You can then call it with the string you want to search for.
The actual implementation would look like this:
ScanFile:
public override ScanItem Seach(string s)
{
if (Name.Contains(s))
return this;
return null;
}
ScanDir:
public override ScanItem Seach(string s)
{
var results = Items.Select(i => i.Seach(s)).Where(i => i != null).ToList();
if (results.Any())
{
var result = (ScanDir)MemberwiseClone();
result.Items = results;
return result;
}
return null;
}
The implementation in ScanFile is easy: if the file matches, return it, else return null. In ScanDir, call Search on all child items recursively. If any of them returned non-null, create a copy of the current object and set the Items of the copy only to those that matched. If none matched, return null.
Note that this will search only through the names of files, not directories. But if you want to do that, such modification is going to be straight-forward.
You should treat the directories a little different because now, if the root directory does not meet the criteria the routine will exit immediately.
Try this: change your ScanItem a little:
public class ScanItem {
...
public virtual bool IsDirectory { get; }
...
}
add this to your scanFile:
public class ScanFile : ScanItem {
public override bool IsDirectory {
get { return false; }
}
}
and this to your scanDir:
public class ScanDir : ScanItem {
public List<ScanItem> Items { get; set; }
public ScanDir() {
Items = new List<ScanItem>();
}
public ScanDir CopyWithoutChildren() {
return new ScanDir() {
FullPath = this.FullPath,
ModifiedDate = this.ModifiedDate,
CreatedDate = this.CreatedDate,
Attributes = this.Attributes,
Size = this.Size
};
}
public override bool IsDirectory {
get { return true; }
}
}
Now do the filtering on the files, omitting empty directories:
public List<ScanItem> search(List<ScanItem> items) {
var filter = new List<ScanItem>();
foreach(var item in items) {
if(item.IsDirectory) {
List<ScanItem> potential = search(((ScanDir)item).Items);
if(potential.Count > 0) {
ScanDir dir = ((ScanDir)item).CopyWithoutChildren();
dir.Items.AddRange(potential);
filter.Add(dir);
}
} else {
if(!item.FullPath.Contains("stringIwantToLookFor")) continue;
filter.Add(item);
}
}
return filter;
}
I didn't test it, but I guess that should do what you want.
I realized my comment to your post might not have been descriptive enough, so I've written some C#-ish pseudocode to demonstrate what I was getting at.
Here's an example of using the Visitor pattern to implement search in a polymorphic, loosely-coupled way:
interface FilesystemVistor
{
void Visit (FilesystemItem item);
}
interface FilesystemItem
{
void Accept(FilesystemVistor visitor);
string Name;
}
class Directory : FilesystemItem
{
private FilesystemItem[] _children;
public void Accept(FilesystemVistor visitor) {
visitor.Visit(this);
foreach(FilesystemItem item in _children)
{
visitor.Visit(item);
}
}
}
class File : FilesystemItem
{
public void Accept(FilesystemVistor visitor) {
visitor.Visit(this);
}
}
class FilesystemSearcher : FilesystemVistor
{
private List<string> _results;
public void Visit(FilesystemItem item) {
if (item.Name == "Foo") { _results.Add(item.Name); }
}
}
This "visitor pattern"-based design will allow you to implement any kind of search without having the search algorithm having to "know" anything about the structure of the file system and the file system doesn't need an extra property like "IsDirectory" to expose its implementation details.
so If I am looking for the files that contain foo this method will populate the files that contain foo in the list 'newList' . I would have to set that list equal to a new list before calling that method. I am obviously missing basic implementation such as changing foo for a parameter etc. I am also missing to remove the empty directories I am working on that.
private List<ScanDir> history = new List<ScanDir>();
private ScanDir LastDir;
private List<ScanItem> newList = new List<ScanItem>();
public void Search(List<ScanItem> allItems) //adds files that contain foo
{
bool updateLastDir = false;
foreach(ScanItem s in allItems)
{
if (updateLastDir)
{
history = (from a in history
select a).Distinct().ToList();
LastDir = null;
for (int i = history.Count - 1; i >= 0; i--)
{
if (history[i].FullPath == Directory.GetParent(s.FullPath).ToString())
{
LastDir = history[i];
break;
}
}
updateLastDir = false;
}
if (s.IsDirectory)
{
var temp = new ScanDir { FullPath = s.FullPath, IsDirectory = true, comparePath = s.comparePath, Attributes = s.Attributes };
if (LastDir == null)
{
newList.Add(temp);
}
else
{
LastDir.Items.Add(temp);
}
LastDir = temp;
history.Add(LastDir);
Search(((ScanDir)s).Items);
history.RemoveAt(history.Count - 1);
updateLastDir = true;
}
else
{
if (s.Name.Contains("Foo")) // then add it
{
if (LastDir == null)
newList.Add(s);
else
LastDir.Items.Add(s);
}
}
}
}

How to Create a Class That Can Have Parent and Child Relationship

I have seen quite a few articles on here about my question but none really answer what I am asking. I am creating a class of my Branch objects that you can envision as just like the TreeNode objects of the TreeView control. Each Branch can have any number of Branch children below (and therefore above) it. Here is my rather simple class:
public class Branch {
public string Name { get; set; }
public string Link { get; set; }
public Branch Parent { get; private set; }
public List<Branch> Children { get; set; }
internal Branch(string Name, string Link) {
this.Name = Name;
this.Link = Link;
this.Children = new List<Branch>();
} // Branch - Constructor - Overload
internal Branch(string Name, string Link, List<Branch> Children) {
this.Name = Name;
this.Link = Link;
this.Children = Children;
this.Children.ForEach(delegate(Branch branch) {
branch.Parent = this;
});
} // Branch - Constructor - Overload
public bool HasChildren {
get { return this.Children.Count > 0; }
} // HasChildren - Property - ReadOnly
public string Path {
get {
string Result = "";
Branch parent = this;
while (parent != null) {
Result = string.Format("{0}/{1}", parent.Name, Result);
parent = parent.Parent;
} // while stepping up the tree
return string.IsNullOrWhiteSpace(Result) ? "" : Result.Substring(0, Result.Length - 1);
} // get
} // Path - Property - ReadOnly
This works GREAT if I Add children at the time of instantiation like the following:
List<Branch> Branches = new List<Branch>() {
new Branch("First", "#"),
new Branch("Second", "#"),
new Branch("Third", "#", new List<Branch>() {
new Branch("ThirdSub1", "#"),
new Branch("ThirdSub2", "#")
}),
new Branch("Fourth", "#"),
new Branch("Fifth", "#"),
new Branch("Sixth", "#", new List<Branch>() {
new Branch("SixthSub1", "#"),
new Branch("SixthSub2", "#", new List<Branch>() {
new Branch("SixthSub2Sub1", "#"),
new Branch("SixthSub2Sub2", "#"),
new Branch("SixthSub2Sub3", "#", new List<Branch>() {
new Branch("Deep Deep Deep Undercover", "#"),
}),
}),
}),
new Branch("Seventh", "#"),
new Branch("Eighth", "#"),
};
But if I do the following:
List<Branch> Branches = new List<Branch>();
Branch Test = Branches.Add(new Branch("Something", ""));
Test.Children.Add(new Branch("Child Here", ""));
The "Child Here" node does NOT have a Parent associated with it. Thus it is broken and of course the Path property doesn't work property.
I thought I could override the List's Add method but that is not allowed. What is the best way to handle this? Currently I am not creating my own Collection Class like MyBranches, which I like, but if there is a way of doing what I need while implementing IList or ISet or Collection, then I am willing to do so. But please provide an example.
Thanks!
Just for people in the future looking for this same solution, here is the full class:
public class Branch {
public string Name { get; set; }
public string Link { get; set; }
public Branch Parent { get; set; }
public TreeBranches Children { get; private set; }
internal Branch(string Name, string Link) {
this.Name = Name;
this.Link = Link;
this.Children = new TreeBranches(this);
} // Branch - Constructor - Overload
internal Branch(string Name, string Link, TreeBranches Children) {
this.Name = Name;
this.Link = Link;
this.Children = Children;
this.Children.ToList().ForEach(delegate(Branch branch) {
branch.Parent = this;
});
} // Branch - Constructor - Overload
/// <summary>
/// Returns a boolean indicating if the given Branch has any child Branches.
/// </summary>
public bool HasChildren {
get { return this.Children.Count > 0; }
} // HasChildren - Property - ReadOnly
/// <summary>
/// Gets the path from the oldest ancestor to the current Branch.
/// </summary>
public string Path {
get {
string Result = "";
Branch parent = this;
while (parent != null) {
Result = string.Format("{0}/{1}", parent.Name, Result);
parent = parent.Parent;
} // while stepping up the tree
return string.IsNullOrWhiteSpace(Result) ? "" : Result.Substring(0, Result.Length - 1);
} // get
} // Path - Property - ReadOnly
} // Branch - Class
public class TreeBranches : IList<Branch> {
private List<Branch> branches = new List<Branch>();
private Branch owner;
public TreeBranches() {
this.owner = null;
}
public TreeBranches(Branch owner) {
this.owner = owner;
}
public void Add(Branch branch) {
branch.Parent = this.owner;
this.branches.Add(branch);
}
#region Standard IList Method Implementation
IEnumerator<Branch> IEnumerable<Branch>.GetEnumerator() { return this.branches.GetEnumerator(); }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return this.branches.GetEnumerator(); }
public int IndexOf(Branch item) { return this.branches.IndexOf(item); }
public void Insert(int index, Branch item) { this.branches.Insert(index, item); }
public void RemoveAt(int index) { this.branches.RemoveAt(index); }
public Branch this[int index] {
get { return this.branches[index]; }
set { this.branches[index] = value; }
}
public void Clear() { this.branches.Clear(); }
public bool Contains(Branch item) { return this.branches.Contains(item); }
public void CopyTo(Branch[] array, int arrayIndex) { this.branches.CopyTo(array, arrayIndex); }
public int Count { get { return this.branches.Count(); } }
public bool IsReadOnly { get { return this.IsReadOnly; } }
public bool Remove(Branch item) { return this.branches.Remove(item); }
#endregion Standard IList Method Implementation
} // TreeBranches - Class
You can derive from Collection<T> instead of List<T>, List<T> is faster, and is optimized for performance, but Collection<T> is more extensible and allows you to override Add() and others.
If performance is not an issue, then use Collection<T>, and if performance is an issue than use Reed's example of containing a List<T> in your own class.

Categories