【问题标题】:Creating Recursive Object from flat list从平面列表创建递归对象
【发布时间】:2015-08-20 22:21:04
【问题描述】:

我有一个清单,如下所示:

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

是否可以把这个数据结构翻译成可以序列化为如下json的格式:

{
    "Menu": {
        "aaa": "aaa",
        "bbb": {
            "ccc": "ccc",
            "ddd": "ddd"
        },
        "eee": "eee"
    }
}

如果答案是肯定的,我该怎么做?

【问题讨论】:

  • 也许您可以提供一些示例代码并将您的问题改写为“这就是我尝试过的,为什么它不起作用?”。这是一个“Give me the codez”问题,并不特别适合 Stack Overflow。
  • @PaulHicks 你读过这个问题的答案了吗?您知道对这类问题有一个通用的回答,但是如果您阅读我的 cmets 以获得答案,您会发现这不是我的期望。无论如何,感谢您为撰写此类有用的评论而付出的努力。
  • 是的,有generic answers很适合教程网站,Yahoo!答案,和其他地方。本站首选specific questions with specific answers

标签: c# json dictionary serialization


【解决方案1】:

您可以考虑使用Json.NET

Json.NET 将任何IDictionary 序列化为JSON key/value pair object - 但转换为Dictionary&lt;string, object&gt; 然后序列化会出现问题,因为.Net 字典是unordered,并且您(可能)想要保留 序列化为 JSON 时 MenuItem 对象的相对顺序。因此,使用 LINQ to JSON 手动转换为 JObject 对象树是有意义的,因为 Json.NET 保留了对象属性的顺序。

你会这样做:

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

(请注意,如果 MenuItem 同时具有非空 Url 和子集合,则此方法会引发异常。)

它使用以下扩展方法:

public static class JsonExtensions
{
    public static void MoveTo(this JToken token, JObject newParent)
    {
        if (newParent == null)
            throw new ArgumentNullException();
        var toMove = token.AncestorsAndSelf().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)]);
    }
}

工作fiddle

【讨论】:

  • 感谢您的精彩回答!
【解决方案2】:

你可以尝试创建一个这样的类

public class MenuItemTr
{

 public MenuItemTr
 {
    this.MenuItems= new List <MenuItem>
 }
public int SiteMenuId {get; set;}
public int ParentId {get; set;}
public string MenuName {get; set;}
public string Url {get; set;}
public int SiteId {get; set;}
public List <MenuItemTr> MenuItems {get; set;}
}

然后解析成一棵树

var MenuItem = menuItems.GenerateTree(c => c.SiteMenuId, c => c.ParentId);

并从此线程Nice & universal way to convert List of items to Tree使用此解决方案

public static IEnumerable<TreeItem<T>> GenerateTree<T, K>(
        this IEnumerable<T> collection,
        Func<T, K> id_selector,
        Func<T, K> parent_id_selector,
        K root_id = default(K))
    {
        foreach (var c in collection.Where(c => parent_id_selector(c).Equals(root_id)))
        {
            yield return new TreeItem<T>
            {
                Item = c,
                Children = collection.GenerateTree(id_selector, parent_id_selector, id_selector(c))
            };
        }
    }
}

【讨论】:

  • 但是这个对象不会像问题中预期的那样被序列化?
  • 我需要将 json 转换为与问题解释相同的格式。通过序列化 MenuItemTr 对象,您将在 json 中有 MenuItems:[] 部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-09-03
  • 2020-10-06
  • 2018-11-16
  • 1970-01-01
  • 2012-08-23
  • 2020-04-08
  • 1970-01-01
相关资源
最近更新 更多