来自邻接列表的JSON?

时间:2012-03-21 08:26:29

标签: c# string json graph

我有一个这样的邻接列表:

A   -  A1
A   -  A2
A   -  A3
A3  - A31
A31 - A311
A31 - A312

我正在尝试获取以下输出:

{
    "name": "A",
    "children": [{
        "name": "A1"
    }, {
        "name": "A2"
    }, {
        "name": "A3",
        "children": [{
            "name": "A31",
            "children": [{
                "name": "A311"
            }, {
                "name": "A312"
            }]
        }]
    }]
};

我有一个包含100K链接的中等大图。这样做的好方法是什么?我认为有一种非常优雅的递归方式,但我不确定如何直接创建JSON字符串。

1 个答案:

答案 0 :(得分:4)

应该有用的东西:

static void Main(string[] args)
{
    var adjList = new List<Link>
    {
        new Link("A","A1"),
        new Link("A","A2"),
        new Link("A","A3"),
        new Link("A3","A31"),
        new Link("A31","A311"),
        new Link("A31","A312"),
    };

    var rootsAndChildren = adjList.GroupBy(x => x.From)
           .ToDictionary(x => x.Key, x => x.Select(y => y.To).ToList());
    var roots = rootsAndChildren.Keys
           .Except(rootsAndChildren.SelectMany(x => x.Value));

    using (var wr = new StreamWriter("C:\\myjson.json"))
    {
        wr.WriteLine("{");
        foreach (var root in roots)
            AppendSubNodes(wr, root, rootsAndChildren, 1);
        wr.WriteLine("};");
    }
}

static void AppendSubNodes(TextWriter wr, string root, 
          Dictionary<string, List<string>> rootsAndChildren, int level)
{
    string indent = string.Concat(Enumerable.Repeat(" ", level * 4));
    wr.Write(indent + "\"name\" : \"" + root + "\"");
    List<string> children;
    if (rootsAndChildren.TryGetValue(root, out children))
    {
        wr.WriteLine(",");
        wr.WriteLine(indent + "\"children\" : [{");
        for (int i = 0; i < children.Count; i++)
        {
            if (i > 0)
                wr.WriteLine(indent + "}, {");
            AppendSubNodes(wr, children[i], rootsAndChildren, level + 1);
        }
        wr.WriteLine(indent + "}]");
    }
    else
    {
        wr.WriteLine();
    }
}

Link是以下类:

class Link
{
    public string From { get; private set; }
    public string To { get; private set; }
    public Link(string from, string to)
    {
        this.From = from;
        this.To = to;
    }
}

上一代码的结果:

{
    "name" : "A",
    "children" : [{
        "name" : "A1"
    }, {
        "name" : "A2"
    }, {
        "name" : "A3",
        "children" : [{
            "name" : "A31",
            "children" : [{
                "name" : "A311"
            }, {
                "name" : "A312"
            }]
        }]
    }]
};

编辑:

如果要检查是否存在图形循环,可以执行以下操作(在创建rootsAndChildren字典之后)

var allNodes = rootsAndChildren.Keys.Concat(rootsAndChildren.SelectMany(x => x.Value)).Distinct();
Func<string, IEnumerable<string>> getSuccessors =
    (x) => rootsAndChildren.ContainsKey(x) ? rootsAndChildren[x] : Enumerable.Empty<string>();

var hasCycles = new Tarjan<string>().HasCycle(allNodes, getSuccessors);

Tarjan是以下类:

// Please note that Tarjan does not detect a cycle due to a node 
// pointing to itself. It's pretty trivial to account for that though...
public class Tarjan<T>
{
    private class Node
    {
        public T Value { get; private set; }
        public int Index { get; set; }
        public int LowLink { get; set; }
        public Node(T value)
        {
            this.Value = value;
            this.Index = -1;
            this.LowLink = -1;
        }
    }
    private Func<T, IEnumerable<T>> getSuccessors;
    private Dictionary<T, Node> nodeMaps;
    private int index = 0;
    private Stack<Node> stack;
    private List<List<Node>> SCC;
    public bool HasCycle(IEnumerable<T> nodes, Func<T, IEnumerable<T>> getSuccessors)
    {
        return ExecuteTarjan(nodes, getSuccessors).Any(x => x.Count > 1);
    }
    private List<List<Node>> ExecuteTarjan(IEnumerable<T> nodes, Func<T, IEnumerable<T>> getSuccessors)
    {
        this.nodeMaps = nodes.ToDictionary(x => x, x => new Node(x));
        this.getSuccessors = getSuccessors;
        SCC = new List<List<Node>>();
        stack = new Stack<Node>();
        index = 0;
        foreach (var node in this.nodeMaps.Values)
        {
            if (node.Index == -1)
                TarjanImpl(node);
        }
        return SCC;
    }
    private IEnumerable<Node> GetSuccessors(Node v)
    {
        return this.getSuccessors(v.Value).Select(x => this.nodeMaps[x]);
    }
    private List<List<Node>> TarjanImpl(Node v)
    {
        v.Index = index;
        v.LowLink = index;
        index++;
        stack.Push(v);
        foreach (var n in GetSuccessors(v))
        {
            if (n.Index == -1)
            {
                TarjanImpl(n);
                v.LowLink = Math.Min(v.LowLink, n.LowLink);
            }
            else if (stack.Contains(n))
            {
                v.LowLink = Math.Min(v.LowLink, n.Index);
            }
        }
        if (v.LowLink == v.Index)
        {
            Node n;
            List<Node> component = new List<Node>();
            do
            {
                n = stack.Pop();
                component.Add(n);
            } while (n != v);
            SCC.Add(component);
        }
        return SCC;
    }
}