Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 52 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
52
Dung lượng
822,18 KB
Nội dung
CHAPTER 8 ■ LINQ TO XML OPERATORS 275 new XElement("BookParticipant", new XAttribute("type", "Editor"), new XElement("FirstName", "Ewan"), new XElement("LastName", "Buckingham")))); IEnumerable<XElement> elements = xDocument.Element("BookParticipants").Descendants("FirstName"); // First, I will display the source elements. foreach (XElement element in elements) { Console.WriteLine("Source element: {0} : value = {1}", element.Name, element.Value); } // Now, I will display the ancestor elements for each source element. foreach (XElement element in elements.Ancestors()) { Console.WriteLine("Ancestor element: {0}", element.Name); } In the previous example, first, I create an XML document. Next, I generate a sequence of FirstName elements. Remember, this Ancestors method is called on a sequence of nodes, not on a single node, so I need a sequence on which to call it. Because I want to be able to display the names of the nodes for identification purposes, I actually build a sequence of elements because elements have names but nodes do not. I then enumerate through the sequence displaying the source elements just so you can see the source sequence. Then, I enumerate on the elements returned from the Ancestors method and display them. Here are the results: Source element: FirstName : value = Joe Source element: FirstName : value = Ewan Ancestor element: BookParticipant Ancestor element: BookParticipants Ancestor element: BookParticipant Ancestor element: BookParticipants As you can see, it displays the two source sequence elements, the two FirstName elements. It then displays the ancestors for each of those two elements. So using the Ancestors operator, I am able to retrieve all of the ancestor elements for each node in a sequence of nodes. In this case, my sequence is a sequence of elements, but that is alright because an element is derived from a node. Remember, do not confuse the Ancestors operator that is called on a sequence of nodes, which I just demonstrated, with the Ancestors method I cover in the previous chapter. Now this example is not quite as impressive as it could be because I needed to expand the code for demonstration purposes. For example, I wanted to capture the sequence of FirstName elements, because I wanted to display them so you could see the source elements in the output. So the state- ment containing the call to the Descendants method and the subsequent foreach block are for this purpose. Then in the second foreach loop, I call the Ancestors operator and display each ancestor element. In reality, in that second foreach loop, I could have called the Ancestors method from the previous chapter on each element in the sequence of FirstName elements and not even called the Ancestors operator I am demonstrating. Listing 8-2 is an example demonstrating what I could have done, which would have accomplished the same result, but without even using the Ancestors operator. Rattz_789-3.book Page 275 Tuesday, October 16, 2007 2:21 PM 276 CHAPTER 8 ■ LINQ TO XML OPERATORS Listing 8-2. The Same Results as Listing 8-1 But Without Calling the Ancestors Operator XDocument xDocument = new XDocument( new XElement("BookParticipants", new XElement("BookParticipant", new XAttribute("type", "Author"), new XElement("FirstName", "Joe"), new XElement("LastName", "Rattz")), new XElement("BookParticipant", new XAttribute("type", "Editor"), new XElement("FirstName", "Ewan"), new XElement("LastName", "Buckingham")))); IEnumerable<XElement> elements = xDocument.Element("BookParticipants").Descendants("FirstName"); // First, I will display the source elements. foreach (XElement element in elements) { Console.WriteLine("Source element: {0} : value = {1}", element.Name, element.Value); } foreach (XElement element in elements) { // Call the Ancestors method on each element. foreach(XElement e in element.Ancestors()) // Now, I will display the ancestor elements for each source element. Console.WriteLine("Ancestor element: {0}", e.Name); } The difference between this example and the previous is that instead of calling the Ancestors operator on the elements sequence in the foreach loop, I just loop on each element in the sequence and call the Ancestors method on it. In this example, I never call the Ancestors operator; I merely call the Ancestors method from the previous chapter. This code produces the same output though: Source element: FirstName : value = Joe Source element: FirstName : value = Ewan Ancestor element: BookParticipant Ancestor element: BookParticipants Ancestor element: BookParticipant Ancestor element: BookParticipants However, thanks to the Ancestors operator and the conciseness of LINQ, this query can be combined into a single, more concise statement as demonstrated in Listing 8-3. Listing 8-3. A More Concise Example of Calling the First Ancestors Prototype XDocument xDocument = new XDocument( new XElement("BookParticipants", new XElement("BookParticipant", new XAttribute("type", "Author"), new XElement("FirstName", "Joe"), new XElement("LastName", "Rattz")), Rattz_789-3.book Page 276 Tuesday, October 16, 2007 2:21 PM CHAPTER 8 ■ LINQ TO XML OPERATORS 277 new XElement("BookParticipant", new XAttribute("type", "Editor"), new XElement("FirstName", "Ewan"), new XElement("LastName", "Buckingham")))); foreach (XElement element in xDocument.Element("BookParticipants").Descendants("FirstName").Ancestors()) { Console.WriteLine("Ancestor element: {0}", element.Name); } In this example, I cut right to the chase and call the Ancestors operator on the sequence of elements returned by the Descendants method. So the Descendants method returns a sequence of elements, and the Ancestors operator will return a sequence of elements containing all ancestors of every element in the sequence it is called on. Since this code is meant to be more concise, it does not display the FirstName elements as the two previous examples did. However, the ancestor elements should be the same. Let’s verify that they are: Ancestor element: BookParticipant Ancestor element: BookParticipants Ancestor element: BookParticipant Ancestor element: BookParticipants And they are! In your production code, you would probably opt for a more concise query like the one I just presented. However, in this chapter, the examples will be more verbose, like Listing 8-1, for demonstration purposes. To demonstrate the second Ancestors prototype, I will use the same basic code as Listing 8-1, except I will change the call to the Ancestors operator so that it includes the parameter BookParticipant so that I only get the elements matching that name. That code looks like Listing 8-4. Listing 8-4. Calling the Second Ancestors Prototype XDocument xDocument = new XDocument( new XElement("BookParticipants", new XElement("BookParticipant", new XAttribute("type", "Author"), new XElement("FirstName", "Joe"), new XElement("LastName", "Rattz")), new XElement("BookParticipant", new XAttribute("type", "Editor"), new XElement("FirstName", "Ewan"), new XElement("LastName", "Buckingham")))); IEnumerable<XElement> elements = xDocument.Element("BookParticipants").Descendants("FirstName"); // First, I will display the source elements. foreach (XElement element in elements) { Console.WriteLine("Source element: {0} : value = {1}", element.Name, element.Value); } Rattz_789-3.book Page 277 Tuesday, October 16, 2007 2:21 PM 278 CHAPTER 8 ■ LINQ TO XML OPERATORS // Now, I will display the ancestor elements for each source element. foreach (XElement element in elements.Ancestors("BookParticipant")) { Console.WriteLine("Ancestor element: {0}", element.Name); } The results now should only include the BookParticipant elements and of course the source elements, but the two BookParticipants elements that are displayed in the first prototype’s example should now be gone: Source element: FirstName : value = Joe Source element: FirstName : value = Ewan Ancestor element: BookParticipant Ancestor element: BookParticipant And they are. AncestorsAndSelf The AncestorsAndSelf operator can be called on a sequence of elements and returns a sequence containing the ancestor elements of each source element and the source element itself. This operator is just like the Ancestors operator except for the fact that it can only be called on elements, as opposed to nodes, and also includes each source element in the returned sequence of ancestor elements. Prototypes The AncestorsAndSelf operator has two prototypes. The First AncestorsAndSelf Prototype public static IEnumerable<XElement> AncestorsAndSelf ( this IEnumerable<XElement> source ) This version of the operator can be called on a sequence of elements and returns a sequence of elements containing each source element itself and its ancestor elements. The Second AncestorsAndSelf Prototype public static IEnumerable<XElement> AncestorsAndSelf<T> ( this IEnumerable<XElement> source, XName name ) This version is like the first, except a name is passed and only those source elements and its ancestors matching the specified name are returned in the output sequence. Examples For an example of the first AncestorsAndSelf prototype, I will use the same basic example I used for the first Ancestors prototype, except I will call the AncestorsAndSelf operator instead of the Ancestors operator, as shown in Listing 8-5. Rattz_789-3.book Page 278 Tuesday, October 16, 2007 2:21 PM CHAPTER 8 ■ LINQ TO XML OPERATORS 279 Listing 8-5. Calling the First AncestorsAndSelf Prototype XDocument xDocument = new XDocument( new XElement("BookParticipants", new XElement("BookParticipant", new XAttribute("type", "Author"), new XElement("FirstName", "Joe"), new XElement("LastName", "Rattz")), new XElement("BookParticipant", new XAttribute("type", "Editor"), new XElement("FirstName", "Ewan"), new XElement("LastName", "Buckingham")))); IEnumerable<XElement> elements = xDocument.Element("BookParticipants").Descendants("FirstName"); // First, I will display the source elements. foreach (XElement element in elements) { Console.WriteLine("Source element: {0} : value = {1}", element.Name, element.Value); } // Now, I will display the ancestor elements for each source element. foreach (XElement element in elements.AncestorsAndSelf()) { Console.WriteLine("Ancestor element: {0}", element.Name); } Just as with the first Ancestors prototype, first, I create an XML document. Next, I generate a sequence of FirstName elements. Remember, this AncestorsAndSelf method is called on a sequence of elements, not on a single element, so I need a sequence on which to call it. I then enumerate through the sequence displaying the source elements just so you can see the source sequence. Then, I enumerate on the elements returned from the AncestorsAndSelf method and display them. If this works as I expect, the results should be the same as the results from the first Ancestors prototype’s example, except now the FirstName elements should be included in the output. Here are the results: Source element: FirstName : value = Joe Source element: FirstName : value = Ewan Ancestor element: FirstName Ancestor element: BookParticipant Ancestor element: BookParticipants Ancestor element: FirstName Ancestor element: BookParticipant Ancestor element: BookParticipants For an example of the second AncestorsAndSelf prototype, I will use the same basic example that I used in the example for the second Ancestors prototype, except, of course, I will change the call from the Ancestors method to the AncestorsAndSelf method, as shown in Listing 8-6. Rattz_789-3.book Page 279 Tuesday, October 16, 2007 2:21 PM 280 CHAPTER 8 ■ LINQ TO XML OPERATORS Listing 8-6. Calling the Second AncestorsAndSelf Prototype XDocument xDocument = new XDocument( new XElement("BookParticipants", new XElement("BookParticipant", new XAttribute("type", "Author"), new XElement("FirstName", "Joe"), new XElement("LastName", "Rattz")), new XElement("BookParticipant", new XAttribute("type", "Editor"), new XElement("FirstName", "Ewan"), new XElement("LastName", "Buckingham")))); IEnumerable<XElement> elements = xDocument.Element("BookParticipants").Descendants("FirstName"); // First, I will display the source elements. foreach (XElement element in elements) { Console.WriteLine("Source element: {0} : value = {1}", element.Name, element.Value); } // Now, I will display the ancestor elements for each source element. foreach (XElement element in elements.AncestorsAndSelf("BookParticipant")) { Console.WriteLine("Ancestor element: {0}", element.Name); } Now, I should only receive the elements named BookParticipant. Here are the results: Source element: FirstName : value = Joe Source element: FirstName : value = Ewan Ancestor element: BookParticipant Ancestor element: BookParticipant Notice that the displayed output from the AncestorsAndSelf method is just the BookParticipant elements, because they are the only elements matching the name I passed. I didn’t even get the source elements themselves, because they didn’t match the name. So the function worked as defined. Call me crazy, but this prototype of the operator seems fairly useless to me. How many levels of elements are you going to have in an XML tree with the same name? If you don’t answer at least two, how will this method ever return the self elements and any ancestor elements? It just doesn’t seem likely to me. Yes, I know; I like symmetrical APIs too. Attributes The Attributes operator can be called on a sequence of elements and returns a sequence containing the attributes of each source element. Prototypes The Attributes operator has two prototypes. Rattz_789-3.book Page 280 Tuesday, October 16, 2007 2:21 PM CHAPTER 8 ■ LINQ TO XML OPERATORS 281 The First Attributes Prototype public static IEnumerable<XAttribute> Attributes ( this IEnumerable<XElement> source ) This version of the operator can be called on a sequence of elements and returns a sequence of attributes containing all the attributes for each source element. The Second Attributes Prototype public static IEnumerable<XAttribute> Attributes ( this IEnumerable<XElement> source, XName name ) This version of the operator is like the first, except only those attributes matching the specified name will be returned in the sequence of attributes. Examples For an example of the first Attributes prototype, I will build the same XML tree I have been building for the previous examples. However, the sequence of source elements I generate will be a little different because I need a sequence of elements with attributes. So I’ll generate a sequence of the BookParticipant elements and work from there, as shown in Listing 8-7. Listing 8-7. Calling the First Attributes Prototype XDocument xDocument = new XDocument( new XElement("BookParticipants", new XElement("BookParticipant", new XAttribute("type", "Author"), new XElement("FirstName", "Joe"), new XElement("LastName", "Rattz")), new XElement("BookParticipant", new XAttribute("type", "Editor"), new XElement("FirstName", "Ewan"), new XElement("LastName", "Buckingham")))); IEnumerable<XElement> elements = xDocument.Element("BookParticipants").Elements("BookParticipant"); // First, I will display the source elements. foreach (XElement element in elements) { Console.WriteLine("Source element: {0} : value = {1}", element.Name, element.Value); } // Now, I will display each source element's attributes. foreach (XAttribute attribute in elements.Attributes()) { Console.WriteLine("Attribute: {0} : value = {1}", attribute.Name, attribute.Value); } Rattz_789-3.book Page 281 Tuesday, October 16, 2007 2:21 PM 282 CHAPTER 8 ■ LINQ TO XML OPERATORS Once I obtain the sequence of BookParticipant elements, I display the source sequence. Then, I call the Attributes operator on the source sequence and display the attributes in the sequence returned by the Attributes operator. Here are the results: Source element: BookParticipant : value = JoeRattz Source element: BookParticipant : value = EwanBuckingham Attribute: type : value = Author Attribute: type : value = Editor As you can see, the attributes are retrieved. For an example of the second Attributes prototype, I will use the same basic example as the previous, except I will specify a name that the attributes must match to be returned by the Attributes operator, as shown in Listing 8-8. Listing 8-8. Calling the Second Attributes Prototype XDocument xDocument = new XDocument( new XElement("BookParticipants", new XElement("BookParticipant", new XAttribute("type", "Author"), new XElement("FirstName", "Joe"), new XElement("LastName", "Rattz")), new XElement("BookParticipant", new XAttribute("type", "Editor"), new XElement("FirstName", "Ewan"), new XElement("LastName", "Buckingham")))); IEnumerable<XElement> elements = xDocument.Element("BookParticipants").Elements("BookParticipant"); // First, I will display the source elements. foreach (XElement element in elements) { Console.WriteLine("Source element: {0} : value = {1}", element.Name, element.Value); } // Now, I will display each source element's attributes. foreach (XAttribute attribute in elements.Attributes("type")) { Console.WriteLine("Attribute: {0} : value = {1}", attribute.Name, attribute.Value); } In the previous code, I specify that the attributes must match the name type. So this should return the same output as the previous example. Pressing Ctrl+F5 returns the following: Source element: BookParticipant : value = JoeRattz Source element: BookParticipant : value = EwanBuckingham Attribute: type : value = Author Attribute: type : value = Editor So I did get the results I expected. Had I specified the name as Type so that the first letter is capital- ized, the two attributes would not have been displayed because the Attributes operator would not have returned those attributes from the source sequence. That demonstrates the case of when the name Rattz_789-3.book Page 282 Tuesday, October 16, 2007 2:21 PM CHAPTER 8 ■ LINQ TO XML OPERATORS 283 doesn’t match, as well as the fact that the name is case-sensitive, which isn’t that surprising since XML is case-sensitive. DescendantNodes The DescendantNodes operator can be called on a sequence of elements and returns a sequence containing the descendant nodes of each element or document. Prototypes The DescendantNodes operator has one prototype. The Only DescendantNodes Prototype public static IEnumerable<XNode> DescendantNodes<T> ( this IEnumerable<T> source ) where T : XContainer This version can be called on a sequence of elements or documents and returns a sequence of nodes containing each source element’s or document’s descendant nodes. This is different from the XContainer.DescendantNodes method in that this method is called on a sequence of elements or documents, as opposed to a single element or document. Examples For this example, I will build the same XML tree I have used for the previous examples, except I will also add a comment to the first BookParticipant element. This is to have at least one node get returned that is not an element. When I build my source sequence of elements, I want some elements that have some descendants, so I will build my source sequence with the BookParticipant elements since they have some descendants, as shown in Listing 8-9. Listing 8-9. Calling the Only DescendantNodes Prototype XDocument xDocument = new XDocument( new XElement("BookParticipants", new XElement("BookParticipant", new XAttribute("type", "Author"), new XComment("This is a new author."), new XElement("FirstName", "Joe"), new XElement("LastName", "Rattz")), new XElement("BookParticipant", new XAttribute("type", "Editor"), new XElement("FirstName", "Ewan"), new XElement("LastName", "Buckingham")))); IEnumerable<XElement> elements = xDocument.Element("BookParticipants").Elements("BookParticipant"); // First, I will display the source elements. foreach (XElement element in elements) { Console.WriteLine("Source element: {0} : value = {1}", element.Name, element.Value); } Rattz_789-3.book Page 283 Tuesday, October 16, 2007 2:21 PM 284 CHAPTER 8 ■ LINQ TO XML OPERATORS // Now, I will display each source element's descendant nodes. foreach (XNode node in elements.DescendantNodes()) { Console.WriteLine("Descendant node: {0}", node); } As is typical with the examples in this section, I built my XML tree and a source sequence of elements. In this case, the source sequence contains the BookParticipant elements. I then call the DescendantNodes operator on the source sequence and display the results: Source element: BookParticipant : value = JoeRattz Source element: BookParticipant : value = EwanBuckingham Descendant node: <! This is a new author > Descendant node: <FirstName>Joe</FirstName> Descendant node: Joe Descendant node: <LastName>Rattz</LastName> Descendant node: Rattz Descendant node: <FirstName>Ewan</FirstName> Descendant node: Ewan Descendant node: <LastName>Buckingham</LastName> Descendant node: Buckingham Notice that not only did I get my descendant elements, I got my comment node as well. Also notice that for each element in the XML document, I ended up with two nodes. For example, there is a node whose value is "<FirstName>Joe</FirstName>" and a node whose value is "Joe". The first node in the pair is the FirstName element. The second node is the XText node for that element. I bet you had forgotten about those automatically created XText objects. I know I did, but there they are. DescendantNodesAndSelf The DescendantNodesAndSelf operator can be called on a sequence of elements and returns a sequence containing each source element itself and each source element’s descendant nodes. Prototypes The DescendantNodesAndSelf operator has one prototype. The Only DescendantNodesAndSelf Prototype public static IEnumerable<XNode> DescendantNodesAndSelf ( this IEnumerable<XElement> source ) This version is called on a sequence of elements and returns a sequence of nodes containing each source element itself and each source element’s descendant nodes. Examples For this example, I will use the same example used for the DescendantNodes operator, except I will call the DescendantNodesAndSelf operator, as shown in Listing 8-10. Rattz_789-3.book Page 284 Tuesday, October 16, 2007 2:21 PM [...]... present: using using using using using using System .Linq; System.Xml; System.Xml .Linq; System.Xml.Schema; System.Xml.XPath; System.Xml.Xsl; Queries In the previous LINQ to XML chapters, I demonstrated the core principles needed to perform XML queries using LINQ to XML However, most of the examples are specifically designed to demonstrate an operator or a property In this section, I want to provide some... pretty spectacular, don’t you think? I just joined three XML documents in a single query Surely you now see the power of LINQ to XML Are you starting to see why LINQ to XML is my favorite part of LINQ? Now how much would you pay? But wait, there’s more! Transformations With LINQ to XML, you can perform XML transformations using two completely different approaches The first approach is to use XSLT via the... for querying XML data In particular, the way the Standard Query Operators can be mingled with LINQ to XML operators lends itself to quite elegant and powerful queries At this point, I have covered just about all there is to know about the building blocks needed for performing LINQ to XML queries In the next chapter, I provide some slightly more complex queries and cover some of the remaining XML necessities... requirement In that chapter, I demonstrated how to query a single node or element for nodes and elements hierarchically related to it In this chapter, I covered doing the same thing with sequences of nodes or elements using the LINQ to XML operators I hope I have made it clear how to perform elementary queries on XML trees using LINQ to XML I believe that this new XML API will prove to be quite useful for querying... validate an XML document against a schema, and I even present an example performing an XPath-style query Referenced Namespaces Examples in this chapter reference the System.Xml, System.Xml.Schema, System.Xml.Xsl, and System Xml.XPath namespaces, in addition to the typical LINQ and LINQ to XML namespaces, System .Linq and System.Xml .Linq Therefore, you will want to add using directives for these if they... will hopefully make querying XML with the LINQ to XML API seem trivial, including some that use the query expression syntax for those of you who prefer it Additionally, the new LINQ to XML API just wouldn’t be complete without a few additional capabilities such as transformation and validation In this chapter, I cover these LINQ to XML leftovers, as well as any other good-to-know information Specifically,... Doe A U03 Dee Linquent D U04 Roger Smith C U05 Jack Sprat B U 06 Rip Van Winkle B ... operator is and how I can integrate it into the LINQ to XML query That seems like it could be very useful Rattz_789-3.book Page 299 Tuesday, October 16, 2007 2:21 PM C HA PTER 8 ■ LINQ TO XM L OPERA TORS Summary In the previous chapter, I covered the new LINQ to XML API that allows you to create, modify, save, and load XML trees Notice I said trees as opposed to documents, because with LINQ to XML, documents... classes, XmlReader and XmlWriter The second approach is to use LINQ to XML to perform the transformation itself by functionally constructing the target XML document and embedding a LINQ to XML query on the source XML document Using XSLT provides the benefit that it is a standard XML technology Tools already exist to assist with writing, debugging, and testing XSLT transformations Additionally, because... ((double)b.Element("bid")) > 50 join u in users.Descendants("user_tuple") on ((string)b.Element("userid")) equals ((string)u.Element("userid")) join i in items.Descendants("item_tuple") on ((string)b.Element("itemno")) equals ((string)i.Element("itemno")) select new { Item = ((string)b.Element("itemno")), Description = ((string)i.Element("description")), User = ((string)u.Element("name")), Date = ((string)b.Element("bid_date")), . conciseness of LINQ, this query can be combined into a single, more concise statement as demonstrated in Listing 8-3. Listing 8-3. A More Concise Example of Calling the First Ancestors Prototype XDocument. method, as shown in Listing 8 -6. Rattz_789-3.book Page 279 Tuesday, October 16, 2007 2:21 PM 280 CHAPTER 8 ■ LINQ TO XML OPERATORS Listing 8 -6. Calling the Second AncestorsAndSelf Prototype XDocument. operator, as shown in Listing 8-10. Rattz_789-3.book Page 284 Tuesday, October 16, 2007 2:21 PM CHAPTER 8 ■ LINQ TO XML OPERATORS 285 Listing 8-10. Calling the Only DescendantNodesAndSelf Prototype XDocument