I've expanded your XML and demonstrate how to get the MoveBack command text. I've also added an example to grab the robot models for a particular manufacturer and list each robot's commands. The first example is broken down to demonstrate how to walk the XML structure to get an element at a time. The second example is done in one query. Of course this depends on how well you know your data. You should use SingleOrDefault
and check for null before using a result if you expect it not to exist. Also, checking for attributes is important if they don't exist. This code assumes the XML is complete.
Regarding the structure of the XML it looks fine. Keeping the CommandText generic allows different commands to be supported. If the commands are always the same they could be their own elements. You could make the model name its own element, but leaving it as is - as an attribute - makes sense.
string input = @"<root>
<Manufacturer ManufacturerName=""Acme"">
<Model ModelName=""RobotOne"">
<CommandText CommandName=""MoveForward"">MVFW</CommandText>
<CommandText CommandName=""MoveBack"">MVBK</CommandText>
</Model>
<Model ModelName=""RobotTwo"">
<CommandText CommandName=""MoveRight"">MVRT</CommandText>
<CommandText CommandName=""MoveLeft"">MVLT</CommandText>
</Model>
</Manufacturer>
<Manufacturer ManufacturerName=""FooBar Inc."">
<Model ModelName=""Johnny5"">
<CommandText CommandName=""FireLaser"">FL</CommandText>
<CommandText CommandName=""FlipTVChannels"">FTVC</CommandText>
</Model>
<Model ModelName=""Optimus"">
<CommandText CommandName=""FirePlasmaCannon"">FPC</CommandText>
<CommandText CommandName=""TransformAndRollout"">TAL</CommandText>
</Model>
</Manufacturer>
</root>";
var xml = XElement.Parse(input);
// get the Manufacturer elements, then filter on the one named "Acme".
XElement acme = xml.Elements("Manufacturer")
.Where(element => element.Attribute("ManufacturerName").Value == "Acme")
.Single(); // assuming there's only one Acme occurrence.
// get Model elements, filter on RobotOne name, get CommandText elements, filter on MoveBack, select single element
var command = acme.Elements("Model")
.Where(element => element.Attribute("ModelName").Value == "RobotOne")
.Elements("CommandText")
.Where(c => c.Attribute("CommandName").Value == "MoveBack")
.Single();
// command text value
string result = command.Value;
Console.WriteLine("MoveBack command: " + result);
// one unbroken query to list each FooBar Inc. robot and their commands
var query = xml.Elements("Manufacturer")
.Where(element => element.Attribute("ManufacturerName").Value == "FooBar Inc.")
.Elements("Model")
.Select(model => new {
Name = model.Attribute("ModelName").Value,
Commands = model.Elements("CommandText")
.Select(c => new {
CommandName = c.Attribute("CommandName").Value,
CommandText = c.Value
})
});
foreach (var robot in query)
{
Console.WriteLine("{0} commands:", robot.Name);
foreach (var c in robot.Commands)
{
Console.WriteLine("{0}: {1}", c.CommandName, c.CommandText);
}
Console.WriteLine();
}
If you decide to use an XDocument instead you'll need to use the Root: xml.Root.Elements(...)