Linq to XML Simple Query

I do not get this thing Link. I can write complex SQL queries and have written several xpaths. I am trying to learn Linq for XML and cannot go through my first attempt, despite the fact that I am reviewing every brief example that I can do with Google.

Given XML:

<Manufacturer ManufacturerName="Acme"> <Model ModelName="RobotOne"> <CommandText CommandName="MoveForward">MVFW</CommandText> <CommandText CommandName="MoveBack">MVBK</CommandText> 

The request input is "Acme", "RobotOne", "MoveBack", I need the output of "MVBK"

Not sure if this is the best way to build XML, how would I do this with elements instead of attributes? There are several manufacturers and models and many codes.

+6
linq linq-to-xml
source share
4 answers

If you are having difficulty with the whole idea of ​​LINQ to XML, I would suggest stepping back a bit and just looking at linq as a whole. I believe linq is very worth the effort and will pay you back many times - especially when we move into the multi-user world.

I would suggest that you forget the XML part for a moment and just think about what linq does - after you get what linq is about, then this will make the specialized material for XML simpler. It helps me think about this with these two considerations:

  • It expresses standard “operators” (extension methods) on everything that implements the IEnumerable <T> interface. In other words, linq is just a method call. Some of the methods accept (Func) in the code, some (First ()) do not.
  • this allows you to care less about how these operators give you the result, and most of all announces your intention .

It’s not easy for us to require that language people let go of the minute (more) specification of how to get the result. That linq basically gives us a way to declare , we want to get the result without specifying exactly how to get it. That's why, IMO, linq is so important to learn, because it is this “decoupling” of intent from the exact “how” that is really the cool part of linq. This basically translates the concept of functional language into the poor old imperative C #.

A useful tutorial for LINQ is to write imperative C # code that will do the same . Do this several times, and suddenly you will see the template that linq does for you. So, for example, consider the declaration of "linq-to-object".

 string[] names= new string[] { "kevin", "tristan", jen" }; var result = names.where(n => n.length() > 3); 

an analog in C # for a linq request would be:

 List<string> list = new List<string>(); foreach(string n in names) { if (n.length > 3) list.add(n); } 

I think that in this example it’s pretty easy to see how linq does the same thing as foreach (actually it’s very close to reality) - but you don’t have all the fluff that is just a goo compiler. There are less problems and more intentions in the linq version. Another way to express it: linq makes boring boilerplate stuff implicit for you , letting your code just show the interesting part.

In a functional expression, the where () method is a higher-order function . This means that it is a function that itself takes or returns a function. Where () takes lambda - an interesting part of the loop . Lambda is an anonymous function in C #, so Where () takes a function, so it is of a higher order . I mention this because this concept is so strong and the key to understanding linq and gives an idea of ​​how linq actually represents a completely new programming model (functional programming), anchored in C #. Getting this "higher order" is very useful for understanding linq.

Working with a direct list <T> I think that queries like the one above are the best way to wrap linq around you. Afer you feel comfortable with straignt L-2-O requests and then look at the XML stuff again - I think you will find that they will make more sense.

With the Reactive Framework released in .NET 4.0, we will see that linq expands not only for pull requests, but also for push scripts. Ie: the collection will generate code when it changes. I am convinced that the concepts of functional programming (of which linq is the C # tunnel) are the most likely ways to get bread and butter that we will handle concurrency and parallel problems, so it is very important to study them, and not just make our code shorter .

+5
source share

Assuming you are using this initialization:

 string xml = @" <Manufacturer ManufacturerName='Acme'> <Model ModelName='RobotOne'> <CommandText CommandName='MoveForward'>MVFW</CommandText> <CommandText CommandName='MoveBack'>MVBK</CommandText> </Model> </Manufacturer>"; XElement topElement = XElement.Parse(xml); 

You can get the result through the following LINQ query:

 string commandText = topElement.Elements("Model") .Where(element => (string)element.Attribute("ModelName") == "RobotOne") .Elements("CommandText") .Where(element => (string)element.Attribute("CommandName") == "MoveBack") .Select(element => element.Value) .FirstOrDefault(); 

If this XML is nested below, you will need more Select / Where combinations.

+4
source share

Not quite the answer, but won't XPath make the code a little easier?

  var result1 = XDocument .Load("test.xml") .XPathSelectElements("/Manufacturer[@ManufacturerName='Acme']/Model[@ModelName='RobotOne']/CommandText[@CommandName='MoveBack']") .FirstOrDefault().Value; 
+2
source share

I expanded your XML and demonstrated how to get MoveBack command text. I also added an example to capture robot models for a specific manufacturer and list all the robot teams. The first example is broken down to demonstrate how to walk through the XML structure to get an element at a time. The second example is executed in one request. Of course, it depends on how well you know your data. You should use SingleOrDefault and check for null before using the result if you expect it to not exist. In addition, checking attributes is important if they do not exist. This code assumes the XML is complete.

As for the XML structure, it looks fine. Saving a universal CommandText allows you to support different commands. If the commands are always the same, they can be their own elements. You can make the model name your own element, but leaving it as it 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 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 XDocument, you will need to use Root: xml.Root.Elements(...)

+1
source share

All Articles