2009-10-23 10 views
2

J'écris un simple analyseur de fichiers XML en utilisant LINQ to XML.Créer un graphe d'objet profond avec Linq to XML, refactoring?

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

Il semble laid et redondant par rapport à la simple approche de bouclage que j'utilisais auparavant (en utilisant System.XML). Y a-t-il un moyen de supprimer les redondances ici?

 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

Vous devriez peut-être fournir le fichier exp.xml ou au moins quelques exemples de données afin que les utilisateurs puissent manipuler les données réelles. – Max

Répondre

1

Il est difficile d'y répondre complètement sans données d'échantillon et types réels, mais je le refactoriserais comme ci-dessous. De l'exemple original, je suppose que nous ne voulons pas jouer avec les constructeurs des entités (Agent etc.), et que nous voulons conserver le modèle séparé "TreeNode<T>", en plaçant nos entités à l'intérieur de l'arbre (plutôt que de changer les entités pour modéliser les choses en tant que collections associées). J'ai aussi supposé que nous pouvons prendre plus de libertés avec TreeNode<T> que nous pouvons avec les entités, donc je l'ai mis en place un constructeur qui accepte IEnumerable<...>, car cela permet d'utiliser des 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 cadre code 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 
         ) 
        ) 
       ) 
      ) 
     ); 
    } 
} 
1

Sans vos classes et la source xml, il est assez difficile de vous fournir le code exact que vous êtes après, 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") 
      })) 
     })) 
    }; 

Compte tenu du 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; } 
}