このコードを試してください(説明については、以下のコードを参照してください)
class Program {
static void Main(string[] args) {
var xml = @"
<Root>
<System>
<ID>1</ID>
<Name>one</Name>
<Monitor>
<ID>3</ID>
<Type>t3</Type>
<Alert>
<ID>5</ID>
<Status>a5</Status>
</Alert>
<Alert>
<ID>6</ID>
<Status>a6</Status>
</Alert>
</Monitor>
</System>
<System>
<ID>2</ID>
<Name>two</Name>
<Monitor>
<ID>4</ID>
<Type>t4</Type>
<Alert>
<ID>7</ID>
<Status>a7</Status>
</Alert>
</Monitor>
</System>
</Root>
";
XElement xmlDoc = XElement.Parse(xml);
// set q to an enumeration of XElements
// where the elements xname is "System"
// the query actually executes the first time q is used
var q = xmlDoc.Elements("System");
foreach (var ele in q) {
// Get the value of the Element with the xname of "ID"
Console.WriteLine(ele.Element("ID").Value);
Console.WriteLine(ele.Element("Name").Value);
// if ele.Elements("Monitor") returns nothing
// then the foreach will be skipped (null-execution)
foreach (var mon in ele.Elements("Monitor")) {
Console.WriteLine(mon.Element("ID").Value);
Console.WriteLine(mon.Element("Type").Value);
foreach (var alert in mon.Elements("Alert")) {
Console.WriteLine(alert.Element("ID").Value);
Console.WriteLine(alert.Element("Status").Value);
}
}
}
}
}
このコードは、XML ドキュメントを 1 回だけ移動します。C# では、LINQ には言語要素 (「select」や「from」など) とライブラリ要素 (XDocument.Elements などの .NET フレームワーク メソッド) の両方が含まれます。2 つを混在させることは問題ありませんが、ステートメントの背後で何が起こっているかを理解している場合にのみ行う必要があります。この場合、XDocument が "System" の XName を持つすべての子要素を返すように要求しています。上記のコードでは、「q」はすべての要素を受け取るのではなく、反復可能な列挙を受け取ります。XDocument の内容は最初の foreach まで横断的ではなく、一度に 1 つの要素のみが検査されるため、q の割り当ては非常に低コストの操作です。これがどのように実装されているかを確認するには、「C# yield return」を検索してください。
「アラート」要素のみに関心がある場合は、次のようにすることができます。
var alerts = xmlDoc.Descendants("Alert")
これは、XName が "Alert" のすべての要素の列挙を返します (XML ドキュメントの階層内の場所に関係なく)。階層を確実にしたい場合は、「where」を使用できます。次に例を示します。
var alerts = xmlDoc.Descendants("Alert")
.Where(ele => (ele.Parent != null) && (ele.Parent.Name == "Monitor"))
.Where(ele => (ele.Parent.Parent != null) && (ele.Parent.Parent.Name == "System"));
foreach (var alert in alerts) {
Console.WriteLine(alert.Element("ID").Value);
Console.WriteLine(alert.Element("Status").Value);
}
同じノードを複数回反復する必要がある場合は、列挙をリストまたは配列に変換することを検討する必要があります。これにより、時間は節約されますが、メモリ使用量が増加します。IEnumerable<> には、この目的のために ".ToArray()" および ".ToList()" という拡張メソッドがあります。