question 32129406

CSharp · Console

/Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;

    public static class JsonExtensions
    {
        public static IEnumerable<JToken> AncestorsAndSelf(this JToken token) // dotnetfiddle.net seems to be loading Json.NET 6.0 not 7.0.  This extension method is not needed in 7.0 
        {
            for (JToken current = token; current != null; current = current.Parent)
            {
                yield return current;
            }
        }
		
        public static void MoveTo(this JToken token, JObject newParent)
        {
            if (newParent == null)
                throw new ArgumentNullException();
            var toMove = AncestorsAndSelf(token).OfType<JProperty>().First(); // Throws an exception if no parent property found.
            if (toMove.Parent != null)
                toMove.Remove();
            newParent.Add(toMove);
        }
    }

    public static class RecursiveEnumerableExtensions
    {
        static bool ContainsNonNullKey<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key)
        {
            if (dictionary == null)
                throw new ArgumentNullException();
            return key == null ? false : dictionary.ContainsKey(key); // Dictionary<int?, X> throws on ContainsKey(null, ...)
        }

        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(); // Prevent multiple enumerations of the incoming enumerable.
            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 class TestClass
    {
        public static void Test()
        {
            var desiredJson = @"{
                ""Menu"": {
                    ""aaa"": ""aaa"",
                    ""bbb"": {
                        ""ccc"": ""ccc"",
                        ""ddd"": ""ddd""
                    },
                    ""eee"": ""eee""
                }
            }";

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

            Console.WriteLine(json);

            if (!JToken.DeepEquals(JToken.Parse(json), JToken.Parse(desiredJson)))
				throw new InvalidOperationException();
		}
		
        public static string CreateJsonFromMenuItems(IList<MenuItem> menuItems)
        {
            return new JObject
            (
                menuItems.ToTree(
                    m => (int?)m.SiteMenuId,
                    m => m.ParentId, m => new JProperty(m.MenuName, m.Url),
                    (parent, child) =>
                    {
                        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);
                    })
            ).ToString();
        }
	}


	public class Program
	{
		public static void Main()
		{
			Console.WriteLine(typeof(JToken).Assembly.FullName);
			Console.WriteLine("");
			TestClass.Test();
		}
	}
;