using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public static class JsonExtensions
public static IEnumerable<JToken> AncestorsAndSelf(this JToken token)
for (JToken current = token; current != null; current = current.Parent)
public static void MoveTo(this JToken token, JObject newParent)
throw new ArgumentNullException();
var toMove = AncestorsAndSelf(token).OfType<JProperty>().First();
if (toMove.Parent != null)
public static class RecursiveEnumerableExtensions
static bool ContainsNonNullKey<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key)
throw new ArgumentNullException();
return key == null ? false : dictionary.ContainsKey(key);
public static IEnumerable<TResult> ToTree<TInput, TKey, TResult>(
this IEnumerable<TInput> collection,
Func<TInput, TKey> idSelector,
Func<TInput, TKey> parentIdSelector,
Func<TInput, TResult> nodeSelector,
Action<TResult, TResult> addMethod)
if (collection == null || idSelector == null || parentIdSelector == null || nodeSelector == null || addMethod == null)
throw new ArgumentNullException();
var list = collection.ToList();
var dict = list.ToDictionary(i => idSelector(i), i => nodeSelector(i));
foreach (var input in list.Where(i => dict.ContainsNonNullKey(parentIdSelector(i))))
addMethod(dict[parentIdSelector(input)], dict[idSelector(input)]);
return list.Where(i => !dict.ContainsNonNullKey(parentIdSelector(i))).Select(i => dict[idSelector(i)]);
public partial class MenuItem
public int SiteMenuId { get; set; }
public int SiteId { get; set; }
public string MenuName { get; set; }
public string Url { get; set; }
public Nullable<int> ParentId { get; set; }
public int CreatedUser { get; set; }
public System.DateTime CreatedDate { get; set; }
public Nullable<int> ModifiedUser { get; set; }
public Nullable<System.DateTime> ModifiedDate { get; set; }
public static void Test()
List<MenuItem> menuItems = new List<MenuItem>();
menuItems.Add(new MenuItem() { SiteMenuId = 1, ParentId = null, MenuName = "Menu", Url = null, SiteId = 1 });
menuItems.Add(new MenuItem() { SiteMenuId = 2, ParentId = 1, MenuName = "aaa", Url = "aaa", SiteId = 1 });
menuItems.Add(new MenuItem() { SiteMenuId = 3, ParentId = 1, MenuName = "bbb", Url = null, SiteId = 1 });
menuItems.Add(new MenuItem() { SiteMenuId = 4, ParentId = 3, MenuName = "ccc", Url = "ccc", SiteId = 1 });
menuItems.Add(new MenuItem() { SiteMenuId = 5, ParentId = 3, MenuName = "ddd", Url = "ddd", SiteId = 1 });
menuItems.Add(new MenuItem() { SiteMenuId = 6, ParentId = 1, MenuName = "eee", Url = "eee", SiteId = 1 });
var json = CreateJsonFromMenuItems(menuItems);
if (!JToken.DeepEquals(JToken.Parse(json), JToken.Parse(desiredJson)))
throw new InvalidOperationException();
public static string CreateJsonFromMenuItems(IList<MenuItem> menuItems)
m => m.ParentId, m => new JProperty(m.MenuName, m.Url),
if (parent.Value == null || parent.Value.Type == JTokenType.Null)
parent.Value = new JObject();
else if (parent.Value.Type != JTokenType.Object)
throw new InvalidOperationException("MenuItem has both URL and children");
child.MoveTo((JObject)parent.Value);
public static void Main()
Console.WriteLine(typeof(JToken).Assembly.FullName);