Создать глубокий граф объектов с Linq to XML, рефакторинг? - PullRequest
2 голосов
/ 23 октября 2009

Я пишу простой анализатор XML-файлов, используя LINQ to XML.

Я хочу иметь объект TreeNode (т.е. простую структуру дерева) для каждого элемента в XML. Я хочу, чтобы каждый элемент был строго напечатан.

Это выглядит уродливо и избыточно по сравнению с простым циклическим подходом, который я использовал ранее (используя System.XML) Есть ли способ устранить избыточность здесь?

        XElement ops = XElement.Load(@"c:\temp\exp.xml");
        Tree<Element> domain = new Tree<Element>();
        domain.Root = new TreeNode<Element>();
        var cells =
                    from cell in ops.Elements("cell")
                    select new
                    {
                        TreeNodeObj = new TreeNode<Element>
                            (new Cell((string)cell.Attribute("name"), (string)cell.Attribute("name"), null)),
                        XElem = cell
                    };
        foreach (var cell in cells)
        {
            domain.Root.AddChild(cell.TreeNodeObj);
            var agents =
                    from agent in cell.XElem.Elements("agent")
                    select new
                    {
                        TreeNodeObj = new TreeNode<Element>
                            (new Agent((string)agent.Attribute("name"), (string)agent.Attribute("name"), null)),
                        XElem = agent
                    };
            foreach (var agent in agents)
            {
                cell.TreeNodeObj.AddChild(agent.TreeNodeObj);
                var nas =
                    from na in agent.XElem.Elements("node-agent")
                    select new
                    {
                        TreeNodeObj = new TreeNode<Element>
                            (new NodeAgent((string)na.Attribute("name"), (string)na.Attribute("name"), null)),
                        XElem = agent
                    };
                foreach (var na in nas)
                {
                    agent.TreeNodeObj.AddChild(na.TreeNodeObj);
                }
            }
        }

Ответы [ 2 ]

1 голос
/ 27 октября 2009

Трудно ответить на этот вопрос полностью без примеров данных и фактических типов, но я бы реорганизовал их, как показано ниже.

Исходя из исходного примера, я предполагаю, что мы не хотим связываться с конструкторами сущностей (Agent и т. Д.) И что мы хотим сохранить отдельную модель "TreeNode<T>", помещая наши сущности внутри дерева (вместо изменения сущностей для моделирования вещей как связанных коллекций). Я также предположил, что с TreeNode<T> мы можем получить больше свободы, чем с сущностями, поэтому я представил конструктор, который принимает IEnumerable<...>, поскольку это позволяет использовать с подзапросами LINQ:

XElement ops = XElement.Load(@"c:\temp\exp.xml");
Tree<Element> domain = new Tree<Element>(
    from cell in ops.Elements("cell")
    select new TreeNode<Element>(
        new Cell(
            (string)cell.Attribute("name"),
            (string)cell.Attribute("name"), null
        ),
        from agent in cell.Elements("agent")
        select new TreeNode<Element>(
            new Agent(
                (string)agent.Attribute("name"),
                (string)agent.Attribute("name"), null
            ),
            from na in agent.Elements("node-agent")
            select new TreeNode<Element>(
                new NodeAgent(
                    (string)na.Attribute("name"),
                    (string)na.Attribute("name"), null
                )
            )
        )
    )
);

С кодом структуры ниже:

using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
class Tree<T>
{
    public TreeNode<T> Root { get; set; }
    public Tree() { }
    public Tree(IEnumerable<TreeNode<T>> children)
    {
        Root = new TreeNode<T>(children);
    }
}
class TreeNode<T>
{
    private List<TreeNode<T>> children;
    public IList<TreeNode<T>> Children
    {
        get
        {
            if (children == null) children = new List<TreeNode<T>>();
            return children;
        }
    }
    private readonly T value;
    public TreeNode() { }
    public TreeNode(T value) { this.value = value; }
    public TreeNode(T value, IEnumerable<TreeNode<T>> children)
            : this(children)
    {
        this.value = value;
    }
    public TreeNode(IEnumerable<TreeNode<T>> children)
    {
        children = new List<TreeNode<T>>(children);
    }
}
class Element { }
class Cell : Element {
    public Cell(string x, string y, string z) { }
}
class Agent : Element {
    public Agent(string x, string y, string z) { }
}
class NodeAgent : Element {
    public NodeAgent(string x, string y, string z) { }
}
static class Program
{
    static void Main()
    {
        XElement ops = XElement.Load(@"c:\temp\exp.xml");
        Tree<Element> domain = new Tree<Element>(
            from cell in ops.Elements("cell")
            select new TreeNode<Element>(
                new Cell(
                    (string)cell.Attribute("name"),
                    (string)cell.Attribute("name"), null
                ),
                from agent in cell.Elements("agent")
                select new TreeNode<Element>(
                    new Agent(
                        (string)agent.Attribute("name"),
                        (string)agent.Attribute("name"), null
                    ),
                    from na in agent.Elements("node-agent")
                    select new TreeNode<Element>(
                        new NodeAgent(
                            (string)na.Attribute("name"),
                            (string)na.Attribute("name"), null
                        )
                    )
                )
            )
        );
    }
}
1 голос
/ 26 октября 2009

Без ваших классов и исходного XML-кода довольно сложно предоставить вам точный код, который вам нужен, но вот как мне нравится структурировать мой XML-анализ:

XDocument d = XDocument.Parse(@"<a id=""7""><b><c name=""foo""/><c name=""bar""/></b><b/><b2/></a>");
var ae = d.Root;

var a = new A
    {
        Id = (int)ae.Attribute("id"),
        Children = new List<B>(ae.Elements("b").Select(be => new B
        {
            Children = new List<C>(be.Elements("c").Select(ce => new C
            {
                Name = (string)ce.Attribute("name")
            }))
        }))
    };

Учитывая xml:

<a>
  <b>
    <c name="foo"/>
    <c name="bar"/>
  </b>
  <b/>
  <b2/>
</a>

и классы:

class A
{
    public int Id { get; set; }
    public List<B> Children { get; set; }
}
class B
{
    public List<C> Children { get; set; }
}
class C
{
    public string Name { get; set; }
}
...