【问题标题】:Generic Tree to JSON通用树到 JSON
【发布时间】:2016-03-25 02:26:04
【问题描述】:

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

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

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

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

这就是我卡住的地方。我现在需要将 IEnumerableNodes 集合转换为分层 JSON。

我的第一次尝试只是将它直接扔给 JSON 序列化程序,但这不起作用(我也没有真正期望它),因为它是一组通用的 Node 对象。 Node 对象上有一个 Value 属性,它将返回一个 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 在这方面非常简单,它只是人名和头衔。它甚至可以是每个员工的一个字符串。

【问题讨论】:

  • Node 可以序列化吗?如果是这样,听起来原始的 Node 对象将具有您想要的结构。 SelfAndDescendants 听起来像是使结构变平。如果您提供 NodePerson 的代码以及所需的 JSON 结构以供其他人帮助,将会很有帮助。
  • @Layoric 我更新了我的问题以包含有关 Node 和 Person 类的更多信息。任何帮助,将不胜感激。当 JSON 只需要分层结构中的一堆人名时,我不知道如何处理这个复杂的 Node 类。我考虑过创建一个更简单的类,然后移动所有数据以便它可以序列化,但我不确定。
  • 你可以控制Node类吗?
  • @GerardSexton 是的,我可以根据需要进行修改。

标签: c# json generics tree json.net


【解决方案1】:

是的,考虑到您的限制,我想说创建自定义 JsonConverter 是适合这种情况的解决方案。实际上写起来非常简单,只要Node&lt;T&gt; 具有至少允许对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

【讨论】:

  • 太棒了。这正是我所需要的,感谢您提供代码示例。我能够快速将它们放置到位,并且 JSON 虽然不完美,但我可以处理小细节。
  • 由于某种原因,这被作为 XML 返回......知道为什么会这样吗? “这个 XML 文件似乎没有任何与之关联的样式信息..” ...我使用 return Request.CreateResponse(HttpStatusCode.OK, json) .. 方法返回类型是 HttpResponseMessage 当我在调试器中查看 json 变量时JSON 可视化工具看起来是正确的。 Fiddler 还能够将响应解析为有效的 JSON 树。但它在浏览器 (Chrome) 中以 &lt;string xmlns="http://schemas.microsoft.com/2003/10/Serialization/"&gt; 开头
  • 哦,您使用的是Web API?在这种情况下,当您创建响应时,请确保将 JSON 放入 StringContent 对象中,如下所示:HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK); response.Content = new StringContent(json, Encoding.UTF8, "application/json"); 这将阻止 Web API 尝试对其进行双重序列化(无论是 XML 还是 JSON)。
  • 是的,Web API。我按照你说的重写了它,现在它正在返回 JSON 内容。谢谢。
  • 可以毫不费力地将这段代码改编成我自己的树。谢谢。
【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-24
    • 2015-03-30
    • 1970-01-01
    • 1970-01-01
    • 2012-01-01
    • 2017-09-03
    相关资源
    最近更新 更多