反序列化 JSON 树结构并设置父级

本文关键字:设置 结构 JSON 反序列化 | 更新日期: 2023-09-27 18:34:50

这似乎是一个非常基本的问题,但我正在努力寻找一个优雅的解决方案。我有一个 Node 类,用于构建树结构。然后使用 JsonConvert.SerializeObject(..) 将其序列化为 JSON。为了防止序列化时循环引用,我在 Parent 属性上放置了一个 JsonIgnore 属性。

这显然意味着父节点不会作为生成的 JSON 输出中每个节点的一部分进行序列化。

当我反序列化相同的 JSON 字符串时,我希望 Node 对象分配了正确的父对象,以便我可以轻松地向上遍历树。实现这一目标的最干净、最简单的方法是什么?

[JsonObject]
public class Node : IEnumerable<Node>
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    [JsonIgnore]
    public Node Parent { get; private set; }
    [JsonProperty("Children")]
    private readonly Dictionary<Guid, Node> _children = new Dictionary<Guid, Node>();
    public Node()
    {
        Id = Guid.NewGuid();
    }
    public void Add(Node departmentNode)
    {
        if (node.Parent != null)
        {
            node.Parent._children.Remove(node.Id);
        }
        node.Parent = this;
        _children.Add(node.Id, node);
    }
    public IEnumerator<Node> GetEnumerator()
    {
        return _children.Values.GetEnumerator();
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

反序列化 JSON 树结构并设置父级

您可以完全摆脱父级,并在需要查找时使用类似FindParent(node.Id)的东西。

如果这不可行(应该是(,并且您需要有一个父引用,我的建议是遍历树并在反序列化后设置父引用。

我为解决这个问题所做的是忽略父级的序列化并实现一个名为 Children 的公共属性,该属性设置我的私有 ChildrenDict 集合。当我将子项添加到专用字典时,我还设置了每个子项的 Parent 属性。

就个人而言,我不喜欢用特定于 JSON 的属性污染我的数据类,因为我喜欢有一个独立于序列化程序的干净设计。

话虽如此,最终解决方案不使用 JsonIgnoreAttribute 标签并定义:

  • 由 JSON 反序列化程序使用的私有无参数构造函数
  • 私有属性(被 JSON 序列化程序忽略(
  • 一个公共的 GetParent(( 方法(供您自己使用(
  • 父级为参数的公共构造函数(供您自己使用(

也可以定义一个 SetParent(( 方法,尽管在我的代码中我根本不需要它。

此代码使用 NewtonsoftJson 序列化和 DotNET 4.5.2 进行了测试

using System.Collections.Generic;
using System.Linq;
namespace JsonSerializableNode
{
    public class Node
    {
        private Node() { } // used for deserializing
        public Node(string name, Node parent) // used everywhere else in your code
        {
            Name = name;
            Parent = parent;
        }
        public string Name { get; set; }
        private Node Parent { get; set; }
        public Node GetParent()
        {
            return Parent;
        }
        public Node[] Children
        {
            get
            {
                return ChildrenDict.Values.ToArray();
            }
            set
            {
                ChildrenDict.Clear();
                if (value == null || value.Count <= 0) return;
                foreach (Node child in value)
                    Add(child);
            }
        }
        // One could use a typed OrderedDictionary here, since Json lists guarantee the order of the children:
        private Dictionary<string, Node> ChildrenDict { get; } = new Dictionary<string, Node>();
        public Node Add(Node child)
        {
            ChildrenDict.Add(child.Name, child);
            child.Parent = this;
            return child;
        }
        public Node Get(string name)
        {
            return ChildrenDict[name];
        }
        public bool Remove(string name)
        {
            return ChildrenDict.Remove(name);
        }
    }
}