JSON 的通用树

Generic Tree to JSON

我有一个代表组织结构图中员工的通用树结构。

树由自定义 Node<Person> 对象的图表组成,这些对象相互引用,其他属性(如级别)显示它们在树、父级、兄弟姐妹等中的级别。

我必须序列化这个组织结构图的一部分,从一个特定的人向下到他们下面的每个人,我在名为 SelfAndDescendants() 的节点对象上有一个方法,return 是一个 IEnumerable<Node<Person>>

所以基本上我在树中找到特定人的 Node,然后将他们和他们的所有后代放在 IEnumerable 中。这部分工作正常。

这就是我被困的地方。我现在需要将 IEnumerableNodes 放入分层 JSON.

我的第一次尝试只是将它直接扔给 JSON 序列化程序,但这不起作用(我也没有真正期望它起作用),因为它是一组通用 Node 对象。在 Node 对象上有一个 Value 属性 将 return 一个 Person 对象......这是我需要进入 JSON(只是名字)。

string json = JsonConvert.SerializeObject(personNode.SelfAndDescendants.ToList());

这显然是在尝试序列化一个 List<Node<Person>>,这不是我需要的。 JSON return 所需要的只是一个具有简单 Person 对象 Name 的分层格式。没有别的。

我是否必须在循环中手动执行任何操作才能构建自定义 JSON 和 return?

这不是 this post 的副本,因为我在这里处理的是通用递归树,而不是简单的通用数据结构。

我是否必须实施自定义 JsonConverter?这如何处理树中的一系列 Node 个对象?

节点 class 具有各种属性,但它基本上看起来像这样:

public class Node<T> : IEqualityComparer, IEnumerable<T>, IEnumerable<Node<T>> {

        public Node(T value) {
            Value = value;
        }

        public Node<T> this[int index] {
            get {
                return _children[index];
            }
        }

        public Node<T> Add(T value, int index = -1) {
            var childNode = new Node<T>(value);
            Add(childNode, index);
            return childNode;
        }

        public IEnumerable<Node<T>> SelfAndDescendants {
            get {
                return this.ToIEnumarable().Concat(Children.SelectMany(c => c.SelfAndDescendants));
            }
        }

}

Person class 只是代表一个人的 POCO class。此 class 已经为系统的另一部分正确地序列化为 JSON。

    [JsonObject]
    public class Person {

        public string Title { get; set; }

        public DateTime DateOfBirth { get; set; }

        [JsonConverter(typeof(StringEnumConverter))]
        public Gender Gender { get; set; }
        public List<StreetAddress> Addresses { get; set; }

        ... etc

}

期望的输出是组织结构图,显示 JSON 中人员的级别。所以他们老板手下的员工,他们老板手下的那个老板,等等

JSON在这方面非常简单,就是人名和头衔。它甚至可以是每个员工一个字符串。

您想通过在 class 和所需成员上添加序列化属性,然后序列化为字符串

来描述如何序列化此 class
string nodes = JsonConvert.SerializeObject<Node<Person>>(personNode);

[JsonObject]
public class Node<T> : IEqualityComparer, IEnumerable<T>, IEnumerable<Node<T>> {

    [JsonProperty]
    public IEnumerable<Node<T>> Children { get { return _children; } }

    ...

}

JSON.NET Serialization Attributes

是的,考虑到您的限制,我认为创建自定义 JsonConverter 是针对这种情况的合适解决方案。它实际上写起来非常简单,只要 Node<T> 具有 public 属性以允许至少对 ValueChildren 进行读取访问。您不必担心循环;当序列化程序通过 JArray.FromObject 调用遍历 Children 集合时,它会为每个子项回调转换器。

我会这样写:

public class OrgChartConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(Node<Person>));
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Node<Person> node = (Node<Person>)value;
        JObject obj = new JObject();
        obj.Add("Name", node.Value.Name);
        obj.Add("Subordinates", JArray.FromObject(node.Children, serializer));
        obj.WriteTo(writer);
    }

    public override bool CanRead
    {
        get { return false; }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

然后,像这样使用转换器:

var settings = new JsonSerializerSettings
{
    Converters = new List<JsonConverter> { new OrgChartConverter() },
    Formatting = Formatting.Indented
};

string json = JsonConvert.SerializeObject(rootNode, settings);

这是一个演示:https://dotnetfiddle.net/BfdFdW