Linq to XML、リファクタリングで深いオブジェクトグラフを作成しますか?
-
06-07-2019 - |
質問
LINQ to XMLを使用して簡単なXMLファイルパーサーを作成しています。
XMLの各要素にTreeNodeオブジェクト(つまり、単純なツリー構造)が必要です。各要素を厳密に型指定します。
以前に使用していた単純なループアプローチ(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);
}
}
}
解決
サンプルデータと実際の型なしでこれに完全に答えることは困難ですが、以下のようにリファクタリングします。
元の例から、エンティティのコンストラクター(Agent
など)を台無しにしたくなく、個別の<!> quot; TreeNode<T>
<! > quot;エンティティをツリー内に配置するモデル(エンティティを変更して、関連するコレクションとして物事をモデル化するのではなく)。また、エンティティを使用するよりも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
)
)
)
)
);
}
}
他のヒント
クラスとソース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; }
}
所属していません StackOverflow