通用树到JSON

时间:2016-03-25 02:26:05

标签: c# json generics tree json.net

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

树包含一个自定义Node<Person>对象的图形,这些对象相互引用,其他属性如Level显示它们在树中的级别,父级,sibilings等。

我必须将此组织结构图的一部分从特定人员序列化到其下面的每个人,并且我在Node对象上有一个名为SelfAndDescendants()的方法,它返回IEnumerable<Node<Person>>

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

我被困的地方。我现在需要将此IEnumerable Nodes集合设置为分层JSON。

我的第一次尝试只是将它直接放在JSON序列化程序中,但这不起作用(我也没想到),因为它是一组通用的Node对象。 Value对象上有一个Node属性,它将返回一个Person对象...这就是我需要进入JSON(只是名称)。

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

这显然是在尝试序列化List<Node<Person>>,这不是我需要的。所有JSON返回需求都是一种分层格式,简单Person对象Name。没别了。

我是否必须在循环中手动执行任何操作以构建自定义JSON并返回该内容?

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

我是否必须实施自定义JsonConverter?这如何与树中的一系列Node对象一起使用?

Node类具有各种属性,但基本上如下所示:

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类只是一个代表一个人的POCO类。该类已经正确地为系统的另一部分序列化为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在这方面非常简单,它只是人名和头衔。它甚至可以只是每个员工一个字符串。

2 个答案:

答案 0 :(得分:2)

是的,鉴于您的约束,我会说创建自定义JsonConverter是适合这种情况的解决方案。只要Node<T>具有允许至少对ValueChildren的读取权限的公共属性,编写实际上非常简单。你不必担心循环;当序列化程序通过Children调用迭代JArray.FromObject集合时,序列化程序将回调每个子项的转换器。

以下是我的写作方式:

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

答案 1 :(得分:1)

您想要通过在类和所需成员上添加序列化属性来描述如何序列化此类,然后序列化为字符串

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