using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Collections.ObjectModel;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public interface IHasParent<TParent> where TParent : class
void OnParentChanging(TParent newParent);
public class ChildCollection<TParent, TChild> : Collection<TChild>
where TChild : IHasParent<TParent>
public ChildCollection(TParent parent)
protected override void ClearItems()
foreach (var item in this)
item.OnParentChanging(null);
protected override void InsertItem(int index, TChild item)
item.OnParentChanging(parent);
base.InsertItem(index, item);
protected override void RemoveItem(int index)
item.OnParentChanging(null);
protected override void SetItem(int index, TChild item)
var oldItem = this[index];
oldItem.OnParentChanging(null);
item.OnParentChanging(parent);
base.SetItem(index, item);
public class MyObject : IHasParent<MyObject>
readonly ChildCollection<MyObject, MyObject> childObjects;
public MyObject() { this.childObjects = new ChildCollection<MyObject, MyObject>(this); }
public string Name { get; set; }
public IList<MyObject> ChildObjects { get { return childObjects; } }
#region IHasParent<MyObject> Members
public MyObject Parent { get; private set; }
public void OnParentChanging(MyObject newParent)
public bool ShouldSerializeChildObjects() { return childObjects.Count > 0; }
public RootObject() { this.Object = new List<MyObject>(); }
public List<MyObject> Object { get; set; }
public static class MyObjectExtensions
public static IEnumerable<MyObject> Descendants(this RootObject obj)
return obj.Object.SelectMany(o => RecursiveEnumerableExtensions.Traverse(o, (o2) => (o2 == null ? new MyObject[0] : o2.ChildObjects)));
public static IEnumerable<MyObject> DescendantsAndSelf(this MyObject obj)
return RecursiveEnumerableExtensions.Traverse(obj, (o) => (o == null ? new MyObject[0] : o.ChildObjects));
public static IEnumerable<MyObject> Descendants(this MyObject obj)
return RecursiveEnumerableExtensions.Traverse(obj, (o) => (o == null ? new MyObject[0] : o.ChildObjects)).Skip(1);
public static class RecursiveEnumerableExtensions
public static IEnumerable<T> Traverse<T>(
Func<T, IEnumerable<T>> children)
var stack = new Stack<IEnumerator<T>>();
stack.Push(children(root).GetEnumerator());
var enumerator = stack.Peek();
if (!enumerator.MoveNext())
yield return enumerator.Current;
stack.Push(children(enumerator.Current).GetEnumerator());
foreach (var enumerator in stack)
public static void Test()
var root = JsonConvert.DeserializeObject<RootObject>(json);
var newJson = JsonConvert.SerializeObject(root, Formatting.Indented);
Console.WriteLine("\nRe-serialized JSON: ");
Console.WriteLine(newJson);
Assert.IsTrue(JToken.DeepEquals(JToken.Parse(json), JToken.Parse(newJson)));
var oldChildren = root.Object.ToDictionary(o => o, o => o.ChildObjects.ToList());
root.Object.ForEach(o => o.ChildObjects.Clear());
foreach (var child in oldChildren.Values.SelectMany(c => c))
Assert.IsTrue(child.Parent == null);
foreach (var obj in root.Object)
foreach (var child in oldChildren[obj])
obj.ChildObjects.Add(child);
foreach (var obj in root.Descendants().Where(o => o.Parent != null).ToList())
parent.ChildObjects.Remove(obj);
Assert.IsTrue(obj.Parent == null);
var temp = new MyObject() { Name = "Temp" };
parent.ChildObjects.Add(temp);
Assert.IsTrue(temp.Parent == parent);
var index = parent.ChildObjects.IndexOf(temp);
parent.ChildObjects[index] = obj;
Assert.IsTrue(obj.Parent == parent);
Assert.IsTrue(temp.Parent == null);
Console.WriteLine("\nAll tests passed.");
private static void ValidateRoot(RootObject root)
foreach (var obj in root.Object)
Assert.IsTrue(obj.Parent == null);
foreach (var child in obj.Descendants())
Assert.IsTrue(child.Parent != null);
Assert.IsTrue(child.ChildObjects.All(c => c.Parent == child));
public static void Main()
Console.WriteLine("Environment version: " + Environment.Version);
Console.WriteLine("Json.NET version: " + typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");
public class AssertionFailedException : System.Exception
public AssertionFailedException() : base() { }
public AssertionFailedException(string s) : base(s) { }
public static class Assert
public static void IsTrue(bool value)
public static void IsTrue(bool value, string message)
throw new AssertionFailedException(message ?? "failed");