将树序列化为Json对象

时间:2013-04-30 07:55:09

标签: c# json serialization tree

我有以下课程:

TreeNode.cs

public class TreeNode : IEnumerable<TreeNode>
{
    public readonly Dictionary<string, TreeNode> _children = new Dictionary<string, TreeNode>();

    public readonly string Id;
    public TreeNode Parent { get; private set; }

    public TreeNode(string id)
    {
        this.Id = id;
    }

    public TreeNode GetChild(string id)
    {
        return this._childs[id];
    }

    public void Add(TreeNode item)
    {
        if (item.Parent != null)
        {
            item.Parent._childs.Remove(item.Id);
        }

        item.Parent = this;
        this._childs.Add(item.Id, item);
    }

    public IEnumerator<TreeNode> GetEnumerator()
    {
        return this._childs.Values.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }

    public int Count
    {
        get { return this._childs.Count; }
    }
}

FolderStructureNode.cs

public class FolderStructureNode : TreeNode
{
    //Some properties such as FolderName, RelativePath etc.
}

因此,当我有一个FolderStructureNode类型的对象时,它本质上是一个树数据结构,其中每个节点代表一个文件夹。 我想将此对象序列化为JsonObject。我试过了 - JavaScriptSerializer和NewtonSoft。在这两种情况下,我都得到一个输出 -

[
  [
    []
  ],
  [
    []
  ],
  []
]

在序列化时,树看起来像这样:

Tree

如何序列化以获取正确的json对象?我是否必须遍历树并自己创建json?

2 个答案:

答案 0 :(得分:1)

正如我在评论中所说,您的TreeNode类被序列化为数组,因为它实现了IEnumerable<TreeNode>。因此,TreeNode序列化时,您唯一能看到的是节点的子节点。这些子节点(当然)也被序列化为一个数组 - 直到最后一个叶节点。叶节点没有子节点,因此被序列化为空数组。这就是为什么你的JSON输出看起来像这样。

您没有确切地指定您想要的JSON输出,但我认为您想要的是这样的:

{
    "Sales Order": { },
    "RFP":
    {
        "2169": { }
    },
    "Purchase Order":
    {
        "2216": { }
    }
}

要实现此目的,您的TreeNode类必须序列化为 object 。在我看来(并假设/暗示你正在使用Newtonsoft Json.NET),你应该编写一个自定义转换器类,它可能如下所示:

class TreeNodeConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        // we can serialize everything that is a TreeNode
        return typeof(TreeNode).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // we currently support only writing of JSON
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        // we serialize a node by just serializing the _children dictionary
        var node = value as TreeNode;
        serializer.Serialize(writer, node._children);
    }
}

然后,您必须使用TreeNode装饰JsonConverterAttribute类,以便序列化程序知道在序列化TreeNode对象时必须使用转换器。

[JsonConverter(typeof(TreeNodeConverter))]
public class TreeNode : IEnumerable<TreeNode>
{
    // ...
}

如果您不使用Json.NET,我无法确切地告诉您该怎么做但如果您实现IDictionary而不是IEnumerable可能会有所帮助,因此序列化器知道您是处理键值对。

答案 1 :(得分:0)

我正在从数据库中为bootstrap treeview准备数据。这对我有用:

public class TreeNode {
    public string id { get; private set; }

    public string text { get; private set; }

    public bool selectable { get; private set; }

    public readonly Dictionary<string, TreeNode> nodes = new Dictionary<string, TreeNode>();

    [JsonIgnore]
    public TreeNode Parent { get; private set; }

    public TreeNode(
        string id,
        string text,
        bool selectable
    ) {
        this.id = id;
        this.text = text;
        this.selectable = selectable;
    }

    public TreeNode GetChild(string id) {
        var child = this.nodes[id];
        return child;
    }

    public void Add(TreeNode item) {
        if(item.nodes != null) {
            item.nodes.Remove(item.id);
        }

        item.Parent = this;
        this.nodes.Add(item.id, item);
    }
}

Buildng树:

var mapping = new Dictionary<string, TreeNode>(StringComparer.OrdinalIgnoreCase);

foreach(var ln in mx) {
    // 
    var ID = (string)ln[0];
    var TEXT = (string)(ln[1]);
    var PARENT_ID = (string)(ln[2]);

    // 
    var node = new TreeNode(ID, TEXT, true);

    if(!String.IsNullOrEmpty(PARENT_ID) && mapping.ContainsKey(PARENT_ID)) {
        var parent = mapping[PARENT_ID];
        parent.Add(node);
    }
    else {
        tree.Add(node);
    }
    mapping.Add(ID, node);
}

var setting = new JsonSerializerSettings {
    Formatting = Formatting.Indented,
    NullValueHandling = NullValueHandling.Ignore,
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
};

// Ignore root element, just childs - tree.nodes.Values
var json = JsonConvert.SerializeObject(tree.nodes.Values, setting);

// Empty dictionary - remove (not needed for valid JSON)
return json.ToString().Replace("\"nodes\": {},", "");