Question

J'écris un analyseur de fichier XML simple en utilisant LINQ to XML.

Je souhaite avoir un objet TreeNode (c'est-à-dire une simple structure en arbre) pour chaque élément du code XML. Je veux que chaque élément soit fortement typé.

Cela a l'air moche et redondant par rapport à la simple approche en boucle que j'utilisais auparavant (en utilisant System.XML). Existe-t-il un moyen de supprimer les licenciements?

        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);
                }
            }
        }
Était-ce utile?

La solution

Il est difficile de répondre complètement à cette question sans des exemples de données et des types réels, mais je le reformulerais comme ci-dessous.

D'après l'exemple original, je suppose que nous ne voulons pas jouer avec les constructeurs des entités (Agent etc.) et que nous voulons conserver les " TreeNode<T> quot; modéliser, en plaçant nos entités dans l’arbre (plutôt que de changer les entités pour modéliser les choses en tant que collections associées). J'ai également supposé que nous pouvions prendre plus de libertés avec IEnumerable<...> qu'avec les entités, j'ai donc introduit un constructeur qui accepte <=>, car cela permet une utilisation avec les sous-requêtes 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
                )
            )
        )
    )
);

Avec le code cadre ci-dessous:

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
                        )
                    )
                )
            )
        );
    }
}

Autres conseils

Sans vos classes et le code source XML, il est assez difficile de vous fournir le code exact que vous recherchez, mais voici comment j'aime structurer mon analyse 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")
            }))
        }))
    };

Étant donné le xml:

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

et les classes:

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; }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top