.NET 3.5 以降を使用していると仮定すると、これはLINQ2XMLの適切な使用法です。
関連付けられた領域を取得し、どの領域が互いに隣接しているかを識別するクエリを作成できます。
// Load your XML File into an XDocument object
var xDoc = XDocument.Load(xmlPath);
// this is your query, in the end result with my a Dictionary with the Surface
// Id attribute as the key and the AdjacentSpaceId as the value
var result = (from x in xDoc.Descendants("AdjacentSpaceId")
select new
{
SurfaceId = (String)x.Parent.Attribute("id"),
SurfaceName = (String)x.Parent.Element("Name"),
AdjacentSpace = (String)x
}).GroupBy(sp => sp.SurfaceId)
.ToDictionary(grp => grp.Key,
grp => grp.Select(value => value.AdjacentSpace));
LINQ のバリエーションに慣れていない場合は、クエリについてもう少し詳しく説明します。クエリ結果を含むローカル フィールドは単に と呼ばれresults
ます。型は、var
式の右側で型を識別するようにコンパイラに指示する別の .NET 3.5 追加機能です。この場合、result
になりますDictionary<String, IEnumerable<String>>
。
クエリの最初の行で:
from x in xDoc.Descendants("AdjacentSpaceId")
基本的に、LINQ に という XML 内のすべてのノードを反復処理するように指示していますAdjacentSpaceId
。これらのノードがどこにあるか、または親ノードが何と呼ばれているかは問題ではありません。LINQのDescendants()
メソッドは、特定のノードを選択するために正確な XML パスを知る必要がなく、名前だけが必要であることを意味するため、この理由で非常に強力です (最後のメモを参照してください)。
クエリの次の行は、クエリから何を返したいかを正確に定義しています。
select new
{
SurfaceId = (String)x.Parent.Attribute("id"),
SurfaceName = (String)x.Parent.Element("Name"),
AdjacentSpace = (String)x
})
この構文を使用するすべての LINQ クエリには、select
ステートメントが必要です (必ずしも select を必要としない代わりに、または追加で使用できるメソッド構文もありますが、私の意見では、この構文の方が習得が容易です)。
select 句では、基本的に、、およびの 3 つのプロパティを持つ新しい匿名型を定義しています。割り当ての 1 つを説明すると、それらすべてを理解できるはずです。SurfaceId
SurfaceName
AdjacentSpace
SurfaceId = (String)x.Parent.Attribute("id")
この行の は、すべてのノードx
を繰り返し処理していた LINQ クエリの最初の行を指しているため、タイプはです。 AdjacentSpaceId
x
XElement
のParent
プロパティはx
、この場合は親ノードを選択しますSurface
。(注: XML のルート ノードがたまたま呼び出された場合、ルート ノードAdjacentSpaceId
の親が null になるため、この行は例外をスローしますが、問題にはならないという安全な仮定のようです。この場合)。onのParent
プロパティXElement
も別のものになりますXElement
。
次の部分は のAttribute
メソッドでXElement
、 というノードの最初のアトリビュートを選択していますid
。
id
したがって、効果的に、すべての親ノードの属性を各ノードに選択していますAdjacentSpaceId
。
最後に、これを文字列にキャストしています。Value
代わりに使用できるプロパティがありますが、Attribute("id")
メソッドが失敗した場合 (「id」という属性がない場合に発生する可能性があります)、Value
プロパティを呼び出すと例外がスローされるため、私の個人的な好みは文字列にキャストすることです。これらの場合、文字列に null を返すだけです。
この select 句の他の部分は実質的に同じです。
このクエリの残りの部分は、事実上個別のクエリです。私はそれらを連鎖させただけでしたが、簡単に分解して単独で配置することができました.
次の作品:
GroupBy(sp => sp.SurfaceId)
LINQ クエリの結果をSurfaceId
プロパティでグループ化しています。スペースが各サーフェスに隣接している場所を知りたいと思われるため、これは重要なステップです。これにより、隣接するすべてのスペースがサーフェスによって効果的にグループ化されます。
慣れていない場合sp => sp.SurfaceId
は、匿名関数またはデリゲートをすばやく簡単に作成するためのラムダ式です。
そして最後の部分は、グループ化された結果を取得し、それらをより使いやすいものに変換します。通常はDictionary<>
、この場合はDictionary<String, IEnumerable<String>>
ToDictionary(grp => grp.Key,
grp => grp.Select(value => value.AdjacentSpace));
Linq-to-Xml オブジェクトの拡張メソッドについては、Descendants()
前述のように非常に便利な場合もありますが、非常に危険な場合もあります。同じ XML 内に同じ名前で目的が異なる複数のノードがある場合、またはツリーの親が異なる場合、Descendants()
はすべてのオブジェクトを返します。特定の名前に一致するノードの一部のみを返したい場合は、最初にElement()
またはElements()
拡張メソッドを使用して適切な親ノードを選択することにより、最初にそれをフィルタリングする必要があります。その後、 を安全に呼び出すことができますDescendants()
。