diff --git a/.openpublishing.redirection.json b/.openpublishing.redirection.json index 1e76b241a0b80..2aaf9616fdd68 100644 --- a/.openpublishing.redirection.json +++ b/.openpublishing.redirection.json @@ -2012,6 +2012,26 @@ "redirect_url": "/dotnet/standard/linq/statically-compiled-queries", "redirect_document_id": true }, + { + "source_path": "docs/csharp/programming-guide/concepts/linq/how-to-stream-xml-fragments-with-access-to-header-information.md", + "redirect_url": "/dotnet/standard/linq/stream-xml-fragments-access-header-information", + "redirect_document_id": true + }, + { + "source_path": "docs/csharp/programming-guide/concepts/linq/linq-to-xml-events.md", + "redirect_url": "/dotnet/standard/linq/linq-xml-events", + "redirect_document_id": true + }, + { + "source_path": "docs/csharp/programming-guide/concepts/linq/mixed-declarative-code-imperative-code-bugs-linq-to-xml.md", + "redirect_url": "/dotnet/standard/linq/mixed-declarative-imperative-code-bugs", + "redirect_document_id": true + }, + { + "source_path": "docs/csharp/programming-guide/concepts/linq/programming-with-nodes.md", + "redirect_url": "/dotnet/standard/linq/program-nodes", + "redirect_document_id": true + }, { "source_path": "docs/csharp/programming-guide/concepts/threading/how-to-use-a-thread-pool.md", "redirect_url": "/dotnet/api/system.threading.threadpool.queueuserworkitem" @@ -5679,6 +5699,26 @@ "redirect_url": "/dotnet/standard/linq/statically-compiled-queries", "redirect_document_id": false }, + { + "source_path": "docs/visual-basic/programming-guide/concepts/linq/how-to-stream-xml-fragments-with-access-to-header-information.md", + "redirect_url": "/dotnet/standard/linq/stream-xml-fragments-access-header-information", + "redirect_document_id": false + }, + { + "source_path": "docs/visual-basic/programming-guide/concepts/linq/linq-to-xml-events.md", + "redirect_url": "/dotnet/standard/linq/linq-xml-events", + "redirect_document_id": false + }, + { + "source_path": "docs/visual-basic/programming-guide/concepts/linq/mixed-declarative-code-imperative-code-bugs-linq-to-xml.md", + "redirect_url": "/dotnet/standard/linq/mixed-declarative-imperative-code-bugs", + "redirect_document_id": false + }, + { + "source_path": "docs/visual-basic/programming-guide/concepts/linq/programming-with-nodes.md", + "redirect_url": "/dotnet/standard/linq/program-nodes", + "redirect_document_id": false + }, { "source_path": "docs/visual-basic/programming-guide/concepts/threading/how-to-use-a-thread-pool.md", "redirect_url": "/dotnet/api/system.threading.threadpool.queueuserworkitem" diff --git a/docs/csharp/programming-guide/concepts/linq/how-to-stream-xml-fragments-with-access-to-header-information.md b/docs/csharp/programming-guide/concepts/linq/how-to-stream-xml-fragments-with-access-to-header-information.md deleted file mode 100644 index 3692fe7685738..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/how-to-stream-xml-fragments-with-access-to-header-information.md +++ /dev/null @@ -1,162 +0,0 @@ ---- -title: "How to stream XML fragments with access to header information (C#)" -ms.date: 07/20/2015 -ms.assetid: 7f242770-b0c7-418d-894b-643215e1f8aa ---- -# How to stream XML fragments with access to header information (C#) -Sometimes you have to read arbitrarily large XML files, and write your application so that the memory footprint of the application is predictable. If you attempt to populate an XML tree with a large XML file, your memory usage will be proportional to the size of the file—that is, excessive. Therefore, you should use a streaming technique instead. - -One option is to write your application using . However, you might want to use LINQ to query the XML tree. If this is the case, you can write your own custom axis method. For more information, see [How to write a LINQ to XML axis method (C#)](./how-to-write-a-linq-to-xml-axis-method.md). - - To write your own axis method, you write a small method that uses the to read nodes until it reaches one of the nodes in which you are interested. The method then calls , which reads from the and instantiates an XML fragment. It then yields each fragment through `yield return` to the method that is enumerating your custom axis method. You can then write LINQ queries on your custom axis method. - - Streaming techniques are best applied in situations where you need to process the source document only once, and you can process the elements in document order. Certain standard query operators, such as , iterate their source, collect all of the data, sort it, and then finally yield the first item in the sequence. If you use a query operator that materializes its source before yielding the first item, you will not retain a small memory footprint. - -## Example - -Sometimes the problem gets just a little more interesting. In the following XML document, the consumer of your custom axis method also has to know the name of the customer that each item belongs to. - -```xml - - - - A. Datum Corporation - - 0001 - - - 0002 - - - 0003 - - - 0004 - - - - Fabrikam, Inc. - - 0005 - - - 0006 - - - 0007 - - - 0008 - - - - Southridge Video - - 0009 - - - 0010 - - - -``` - - The approach that this example takes is to also watch for this header information, save the header information, and then build a small XML tree that contains both the header information and the detail that you are enumerating. The axis method then yields this new, small XML tree. The query then has access to the header information as well as the detail information. - - This approach has a small memory footprint. As each detail XML fragment is yielded, no references are kept to the previous fragment, and it is available for garbage collection. This technique creates many short lived objects on the heap. - - The following example shows how to implement and use a custom axis method that streams XML fragments from the file specified by the URI. This custom axis is written such that it expects a document that has `Customer`, `Name`, and `Item` elements, and that those elements will be arranged as in the above `Source.xml` document. It is a simplistic implementation. A more robust implementation would be prepared to parse an invalid document. - -```csharp -static IEnumerable StreamCustomerItem(string uri) -{ - using (XmlReader reader = XmlReader.Create(uri)) - { - XElement name = null; - XElement item = null; - - reader.MoveToContent(); - - // Parse the file, save header information when encountered, and yield the - // Item XElement objects as they are created. - - // loop through Customer elements - while (reader.Read()) - { - if (reader.NodeType == XmlNodeType.Element - && reader.Name == "Customer") - { - // move to Name element - while (reader.Read()) - { - if (reader.NodeType == XmlNodeType.Element && - reader.Name == "Name") - { - name = XElement.ReadFrom(reader) as XElement; - break; - } - } - - // loop through Item elements - while (reader.Read()) - { - if (reader.NodeType == XmlNodeType.EndElement) - break; - if (reader.NodeType == XmlNodeType.Element - && reader.Name == "Item") - { - item = XElement.ReadFrom(reader) as XElement; - if (item != null) { - XElement tempRoot = new XElement("Root", - new XElement(name) - ); - tempRoot.Add(item); - yield return item; - } - } - } - } - } - } -} - -static void Main(string[] args) -{ - XElement xmlTree = new XElement("Root", - from el in StreamCustomerItem("Source.xml") - where (int)el.Element("Key") >= 3 && (int)el.Element("Key") <= 7 - select new XElement("Item", - new XElement("Customer", (string)el.Parent.Element("Name")), - new XElement(el.Element("Key")) - ) - ); - Console.WriteLine(xmlTree); -} -``` - - This code produces the following output: - -```xml - - - A. Datum Corporation - 0003 - - - A. Datum Corporation - 0004 - - - Fabrikam, Inc. - 0005 - - - Fabrikam, Inc. - 0006 - - - Fabrikam, Inc. - 0007 - - -``` diff --git a/docs/csharp/programming-guide/concepts/linq/linq-to-xml-events.md b/docs/csharp/programming-guide/concepts/linq/linq-to-xml-events.md deleted file mode 100644 index b491b04bdb411..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/linq-to-xml-events.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: "LINQ to XML Events (C#)" -ms.date: 07/20/2015 -ms.assetid: ce7de951-cba7-4870-9962-733eb01cd680 ---- -# LINQ to XML Events (C#) -[!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)] events enable you to be notified when an XML tree is altered. - - You can add events to an instance of any . The event handler will then receive events for modifications to that and any of its descendants. For example, you can add an event handler to the root of the tree, and handle all modifications to the tree from that event handler. - - For examples of [!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)] events, see and . - -## Types and Events - You use the following types when working with events: - -|Type|Description| -|----------|-----------------| -||Specifies the event type when an event is raised for an .| -||Provides data for the and events.| - - The following events are raised when you modify an XML tree: - -|Event|Description| -|-----------|-----------------| -||Occurs just before this or any of its descendants is going to change.| -||Occurs when an has changed or any of its descendants have changed.| - -## Example - -### Description - Events are useful when you want to maintain some aggregate information in an XML tree. For example, you may want maintain an invoice total that is the sum of the line items of the invoice. This example uses events to maintain the total of all of the child elements under the complex element `Items`. - -### Code - -```csharp -XElement root = new XElement("Root", - new XElement("Total", "0"), - new XElement("Items") -); -XElement total = root.Element("Total"); -XElement items = root.Element("Items"); -items.Changed += (object sender, XObjectChangeEventArgs cea) => -{ - switch (cea.ObjectChange) - { - case XObjectChange.Add: - if (sender is XElement) - total.Value = ((int)total + (int)(XElement)sender).ToString(); - if (sender is XText) - total.Value = ((int)total + (int)((XText)sender).Parent).ToString(); - break; - case XObjectChange.Remove: - if (sender is XElement) - total.Value = ((int)total - (int)(XElement)sender).ToString(); - if (sender is XText) - total.Value = ((int)total - Int32.Parse(((XText)sender).Value)).ToString(); - break; - } - Console.WriteLine("Changed {0} {1}", - sender.GetType().ToString(), cea.ObjectChange.ToString()); -}; -items.SetElementValue("Item1", 25); -items.SetElementValue("Item2", 50); -items.SetElementValue("Item2", 75); -items.SetElementValue("Item3", 133); -items.SetElementValue("Item1", null); -items.SetElementValue("Item4", 100); -Console.WriteLine("Total:{0}", (int)total); -Console.WriteLine(root); -``` - -### Comments - This code produces the following output: - -```output -Changed System.Xml.Linq.XElement Add -Changed System.Xml.Linq.XElement Add -Changed System.Xml.Linq.XText Remove -Changed System.Xml.Linq.XText Add -Changed System.Xml.Linq.XElement Add -Changed System.Xml.Linq.XElement Remove -Changed System.Xml.Linq.XElement Add -Total:308 - - 308 - - 75 - 133 - 100 - - -``` - \ No newline at end of file diff --git a/docs/csharp/programming-guide/concepts/linq/mixed-declarative-code-imperative-code-bugs-linq-to-xml.md b/docs/csharp/programming-guide/concepts/linq/mixed-declarative-code-imperative-code-bugs-linq-to-xml.md deleted file mode 100644 index 7863bd5913424..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/mixed-declarative-code-imperative-code-bugs-linq-to-xml.md +++ /dev/null @@ -1,163 +0,0 @@ ---- -title: "Mixed Declarative Code-Imperative Code Bugs (LINQ to XML) (C#)" -ms.date: 07/20/2015 -ms.assetid: fada62d0-0680-4e73-945a-2b00d7a507af ---- -# Mixed Declarative Code/Imperative Code Bugs (LINQ to XML) (C#) -[!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)] contains various methods that allow you to modify an XML tree directly. You can add elements, delete elements, change the contents of an element, add attributes, and so on. This programming interface is described in [Modifying XML Trees (LINQ to XML) (C#)](./in-memory-xml-tree-modification-vs-functional-construction-linq-to-xml.md). If you are iterating through one of the axes, such as , and you are modifying the XML tree as you iterate through the axis, you can end up with some strange bugs. - - This problem is sometimes known as "The Halloween Problem". - -## Definition of the Problem - When you write some code using LINQ that iterates through a collection, you are writing code in a declarative style. It is more akin to describing *what* you want, rather that *how* you want to get it done. If you write code that 1) gets the first element, 2) tests it for some condition, 3) modifies it, and 4) puts it back into the list, then this would be imperative code. You are telling the computer *how* to do what you want done. - - Mixing these styles of code in the same operation is what leads to problems. Consider the following: - - Suppose you have a linked list with three items in it (a, b, and c): - - `a -> b -> c` - - Now, suppose that you want to move through the linked list, adding three new items (a', b', and c'). You want the resulting linked list to look like this: - - `a -> a' -> b -> b' -> c -> c'` - - So you write code that iterates through the list, and for every item, adds a new item right after it. What happens is that your code will first see the `a` element, and insert `a'` after it. Now, your code will move to the next node in the list, which is now `a'`! It happily adds a new item to the list, `a''`. - - How would you solve this in the real world? Well, you might make a copy of the original linked list, and create a completely new list. Or if you are writing purely imperative code, you might find the first item, add the new item, and then advance twice in the linked list, advancing over the element that you just added. - -## Adding While Iterating - For example, suppose you want to write some code that for every element in a tree, you want to create a duplicate element: - -```csharp -XElement root = new XElement("Root", - new XElement("A", "1"), - new XElement("B", "2"), - new XElement("C", "3") -); -foreach (XElement e in root.Elements()) - root.Add(new XElement(e.Name, (string)e)); -``` - - This code goes into an infinite loop. The `foreach` statement iterates through the `Elements()` axis, adding new elements to the `doc` element. It ends up iterating also through the elements it just added. And because it allocates new objects with every iteration of the loop, it will eventually consume all available memory. - - You can fix this problem by pulling the collection into memory using the standard query operator, as follows: - -```csharp -XElement root = new XElement("Root", - new XElement("A", "1"), - new XElement("B", "2"), - new XElement("C", "3") -); -foreach (XElement e in root.Elements().ToList()) - root.Add(new XElement(e.Name, (string)e)); -Console.WriteLine(root); -``` - - Now the code works. The resulting XML tree is the following: - -```xml - - 1 - 2 - 3 - 1 - 2 - 3 - -``` - -## Deleting While Iterating - If you want to delete all nodes at a certain level, you might be tempted to write code like the following: - -```csharp -XElement root = new XElement("Root", - new XElement("A", "1"), - new XElement("B", "2"), - new XElement("C", "3") -); -foreach (XElement e in root.Elements()) - e.Remove(); -Console.WriteLine(root); -``` - - However, this does not do what you want. In this situation, after you have removed the first element, A, it is removed from the XML tree contained in root, and the code in the Elements method that is doing the iterating cannot find the next element. - - The preceding code produces the following output: - -```xml - - 2 - 3 - -``` - - The solution again is to call to materialize the collection, as follows: - -```csharp -XElement root = new XElement("Root", - new XElement("A", "1"), - new XElement("B", "2"), - new XElement("C", "3") -); -foreach (XElement e in root.Elements().ToList()) - e.Remove(); -Console.WriteLine(root); -``` - - This produces the following output: - -```xml - -``` - - Alternatively, you can eliminate the iteration altogether by calling on the parent element: - -```csharp -XElement root = new XElement("Root", - new XElement("A", "1"), - new XElement("B", "2"), - new XElement("C", "3") -); -root.RemoveAll(); -Console.WriteLine(root); -``` - -## Why Can't LINQ Automatically Handle This? - One approach would be to always bring everything into memory instead of doing lazy evaluation. However, it would be very expensive in terms of performance and memory use. In fact, if LINQ and (LINQ to XML) were to take this approach, it would fail in real-world situations. - - Another possible approach would be to put in some sort of transaction syntax into LINQ, and have the compiler attempt to analyze the code and determine if any particular collection needed to be materialized. However, attempting to determine all code that has side-effects is incredibly complex. Consider the following code: - -```csharp -var z = - from e in root.Elements() - where TestSomeCondition(e) - select DoMyProjection(e); -``` - - Such analysis code would need to analyze the methods TestSomeCondition and DoMyProjection, and all methods that those methods called, to determine if any code had side-effects. But the analysis code could not just look for any code that had side-effects. It would need to select for just the code that had side-effects on the child elements of `root` in this situation. - - LINQ to XML does not attempt to do any such analysis. - - It is up to you to avoid these problems. - -## Guidance - First, do not mix declarative and imperative code. - - Even if you know exactly the semantics of your collections and the semantics of the methods that modify the XML tree, if you write some clever code that avoids these categories of problems, your code will need to be maintained by other developers in the future, and they may not be as clear on the issues. If you mix declarative and imperative coding styles, your code will be more brittle. - - If you write code that materializes a collection so that these problems are avoided, note it with comments as appropriate in your code, so that maintenance programmers will understand the issue. - - Second, if performance and other considerations allow, use only declarative code. Don't modify your existing XML tree. Generate a new one. - -```csharp -XElement root = new XElement("Root", - new XElement("A", "1"), - new XElement("B", "2"), - new XElement("C", "3") -); -XElement newRoot = new XElement("Root", - root.Elements(), - root.Elements() -); -Console.WriteLine(newRoot); -``` diff --git a/docs/csharp/programming-guide/concepts/linq/programming-with-nodes.md b/docs/csharp/programming-guide/concepts/linq/programming-with-nodes.md deleted file mode 100644 index d2f9bf53b73d6..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/programming-with-nodes.md +++ /dev/null @@ -1,168 +0,0 @@ ---- -title: "Programming with Nodes (C#)" -ms.date: 07/20/2015 -ms.assetid: c38df0f2-c805-431a-93ff-9103a4284c2f ---- -# Programming with Nodes (C#) -[!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)] developers who need to write programs such as an XML editor, a transform system, or a report writer often need to write programs that work at a finer level of granularity than elements and attributes. They often need to work at the node level, manipulating text nodes, processing instructions, and comments. This topic provides some details about programming at the node level. - -## Node Details - There are a number of details of programming that a programmer working at the node level should know. - -### Parent Property of Children Nodes of XDocument is Set to Null - The property contains the parent , not the parent node. Child nodes of have no parent . Their parent is the document, so the property for those nodes is set to null. - - The following example demonstrates this: - -```csharp -XDocument doc = XDocument.Parse(@""); -Console.WriteLine(doc.Nodes().OfType().First().Parent == null); -Console.WriteLine(doc.Root.Parent == null); -``` - - This example produces the following output: - -```output -True -True -``` - -### Adjacent Text Nodes are Possible - In a number of XML programming models, adjacent text nodes are always merged. This is sometimes called normalization of text nodes. [!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)] does not normalize text nodes. If you add two text nodes to the same element, it will result in adjacent text nodes. However, if you add content specified as a string rather than as an node, [!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)] might merge the string with an adjacent text node. - - The following example demonstrates this: - -```csharp -XElement xmlTree = new XElement("Root", "Content"); - -Console.WriteLine(xmlTree.Nodes().OfType().Count()); - -// this does not add a new text node -xmlTree.Add("new content"); -Console.WriteLine(xmlTree.Nodes().OfType().Count()); - -// this does add a new, adjacent text node -xmlTree.Add(new XText("more text")); -Console.WriteLine(xmlTree.Nodes().OfType().Count()); -``` - - This example produces the following output: - -```output -1 -1 -2 -``` - -### Empty Text Nodes are Possible - In some XML programming models, text nodes are guaranteed to not contain the empty string. The reasoning is that such a text node has no impact on serialization of the XML. However, for the same reason that adjacent text nodes are possible, if you remove the text from a text node by setting its value to the empty string, the text node itself will not be deleted. - -```csharp -XElement xmlTree = new XElement("Root", "Content"); -XText textNode = xmlTree.Nodes().OfType().First(); - -// the following line does not cause the removal of the text node. -textNode.Value = ""; - -XText textNode2 = xmlTree.Nodes().OfType().First(); -Console.WriteLine(">>{0}<<", textNode2); -``` - - This example produces the following output: - -```output ->><< -``` - -### An Empty Text Node Impacts Serialization - If an element contains only a child text node that is empty, it is serialized with the long tag syntax: ``. If an element contains no child nodes whatsoever, it is serialized with the short tag syntax: ``. - -```csharp -XElement child1 = new XElement("Child1", - new XText("") -); -XElement child2 = new XElement("Child2"); -Console.WriteLine(child1); -Console.WriteLine(child2); -``` - - This example produces the following output: - -```xml - - -``` - -### Namespaces are Attributes in the LINQ to XML Tree - Even though namespace declarations have identical syntax to attributes, in some programming interfaces, such as XSLT and XPath, namespace declarations are not considered to be attributes. However, in [!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)], namespaces are stored as objects in the XML tree. If you iterate through the attributes for an element that contains a namespace declaration, you will see the namespace declaration as one of the items in the returned collection. - - The property indicates whether an attribute is a namespace declaration. - -```csharp -XElement root = XElement.Parse( -@""); -foreach (XAttribute att in root.Attributes()) - Console.WriteLine("{0} IsNamespaceDeclaration:{1}", att, att.IsNamespaceDeclaration); -``` - - This example produces the following output: - -```output -xmlns="http://www.adventure-works.com" IsNamespaceDeclaration:True -xmlns:fc="www.fourthcoffee.com" IsNamespaceDeclaration:True -AnAttribute="abc" IsNamespaceDeclaration:False -``` - -### XPath Axis Methods Do Not Return Child White Space of XDocument - [!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)] allows for child text nodes of an , as long as the text nodes contain only white space. However, the XPath object model does not include white space as child nodes of a document, so when you iterate through the children of an using the axis, white-space text nodes will be returned. However, when you iterate through the children of an using the XPath axis methods, white-space text nodes will not be returned. - -```csharp -// Create a document with some white-space child nodes of the document. -XDocument root = XDocument.Parse( -@" - - - - -", LoadOptions.PreserveWhitespace); - -// count the white-space child nodes using LINQ to XML -Console.WriteLine(root.Nodes().OfType().Count()); - -// count the white-space child nodes using XPathEvaluate -Console.WriteLine(((IEnumerable)root.XPathEvaluate("text()")).OfType().Count()); -``` - - This example produces the following output: - -```output -3 -0 -``` - -### XDeclaration Objects are not Nodes - When you iterate through the children nodes of an , you will not see the XML declaration object. It is a property of the document, not a child node of it. - -```csharp -XDocument doc = new XDocument( - new XDeclaration("1.0", "utf-8", "yes"), - new XElement("Root") -); -doc.Save("Temp.xml"); -Console.WriteLine(File.ReadAllText("Temp.xml")); - -// this shows that there is only one child node of the document -Console.WriteLine(doc.Nodes().Count()); -``` - - This example produces the following output: - -```output - - -1 -``` - \ No newline at end of file diff --git a/docs/standard/linq/create-xml-trees.md b/docs/standard/linq/create-xml-trees.md index 7281703b0e09f..06c7586559f6b 100644 --- a/docs/standard/linq/create-xml-trees.md +++ b/docs/standard/linq/create-xml-trees.md @@ -210,7 +210,7 @@ XElement xmlTree1 = new XElement("Root", new XElement("Child1", 1) ); -// Create an element that isn't parented. +// Create an element that's not parented. XElement child2 = new XElement("Child2", 2); // Create a tree and add Child1 and Child2 to it. diff --git a/docs/standard/linq/linq-xml-events.md b/docs/standard/linq/linq-xml-events.md new file mode 100644 index 0000000000000..81912c4f0fb48 --- /dev/null +++ b/docs/standard/linq/linq-xml-events.md @@ -0,0 +1,149 @@ +--- +title: LINQ to XML events - LINQ to XML +description: Learn how to use XML events to monitor changes to an XML tree. +ms.date: 07/20/2015 +dev_langs: + - "csharp" + - "vb" +ms.assetid: ce7de951-cba7-4870-9962-733eb01cd680 +--- + +# LINQ to XML Events (LINQ to XML) + +LINQ to XML events enable you to be notified when an XML tree is altered. + +You can add events to any instance of any . The event handler will then receive events for modifications to that and any of its descendants. For example, you can add an event handler to the root of the tree, and handle all modifications to the tree from that event handler. + +For examples of LINQ to XML events, see and . + +## Types and events + +You use the following types when working with events: + +|Type|Description| +|----------|-----------------| +||Specifies the event type when an event is raised for an .| +||Provides data for the and events.| + +The following events are raised when you modify an XML tree: + +|Event|Description| +|-----------|-----------------| +||Occurs just before this or any of its descendants is going to change.| +||Occurs when an has changed or any of its descendants have changed.| + +## Example: Use events to maintain a proper total when items are changed + +Events are useful when you want to maintain some aggregate information in an XML tree. For example, you may want to maintain an invoice total that's the sum of the line items of the invoice. This example uses events to maintain the total of all of the child elements under the complex element `Items`. + +```csharp +XElement root = new XElement("Root", + new XElement("Total", "0"), + new XElement("Items") +); +XElement total = root.Element("Total"); +XElement items = root.Element("Items"); +items.Changed += (object sender, XObjectChangeEventArgs cea) => +{ + switch (cea.ObjectChange) + { + case XObjectChange.Add: + if (sender is XElement) + total.Value = ((int)total + (int)(XElement)sender).ToString(); + if (sender is XText) + total.Value = ((int)total + (int)((XText)sender).Parent).ToString(); + break; + case XObjectChange.Remove: + if (sender is XElement) + total.Value = ((int)total - (int)(XElement)sender).ToString(); + if (sender is XText) + total.Value = ((int)total - Int32.Parse(((XText)sender).Value)).ToString(); + break; + } + Console.WriteLine("Changed {0} {1}", + sender.GetType().ToString(), cea.ObjectChange.ToString()); +}; +items.SetElementValue("Item1", 25); +items.SetElementValue("Item2", 50); +items.SetElementValue("Item2", 75); +items.SetElementValue("Item3", 133); +items.SetElementValue("Item1", null); +items.SetElementValue("Item4", 100); +Console.WriteLine("Total:{0}", (int)total); +Console.WriteLine(root); +``` + +```vb +Module Module1 + Dim WithEvents items As XElement = Nothing + Dim total As XElement = Nothing + Dim root As XElement = _ + + 0 + + + + Private Sub XObjectChanged( _ + ByVal sender As Object, _ + ByVal cea As XObjectChangeEventArgs) _ + Handles items.Changed + Select Case cea.ObjectChange + Case XObjectChange.Add + If sender.GetType() Is GetType(XElement) Then + total.Value = CStr(CInt(total.Value) + _ + CInt((DirectCast(sender, XElement)).Value)) + End If + If sender.GetType() Is GetType(XText) Then + total.Value = CStr(CInt(total.Value) + _ + CInt((DirectCast(sender, XText)).Value)) + End If + Case XObjectChange.Remove + If sender.GetType() Is GetType(XElement) Then + total.Value = CStr(CInt(total.Value) - _ + CInt((DirectCast(sender, XElement)).Value)) + End If + If sender.GetType() Is GetType(XText) Then + total.Value = CStr(CInt(total.Value) - _ + CInt((DirectCast(sender, XText)).Value)) + End If + End Select + Console.WriteLine("Changed {0} {1}", _ + sender.GetType().ToString(), _ + cea.ObjectChange.ToString()) + End Sub + + Sub Main() + total = root.(0) + items = root.(0) + items.SetElementValue("Item1", 25) + items.SetElementValue("Item2", 50) + items.SetElementValue("Item2", 75) + items.SetElementValue("Item3", 133) + items.SetElementValue("Item1", Nothing) + items.SetElementValue("Item4", 100) + Console.WriteLine("Total:{0}", CInt(total)) + Console.WriteLine(root) + End Sub +End Module +``` + +This example produces the following output: + +```output +Changed System.Xml.Linq.XElement Add +Changed System.Xml.Linq.XElement Add +Changed System.Xml.Linq.XText Remove +Changed System.Xml.Linq.XText Add +Changed System.Xml.Linq.XElement Add +Changed System.Xml.Linq.XElement Remove +Changed System.Xml.Linq.XElement Add +Total:308 + + 308 + + 75 + 133 + 100 + + +``` diff --git a/docs/standard/linq/linq-xml-vs-dom.md b/docs/standard/linq/linq-xml-vs-dom.md index d8dc1a8703807..a48c14576ba37 100644 --- a/docs/standard/linq/linq-xml-vs-dom.md +++ b/docs/standard/linq/linq-xml-vs-dom.md @@ -181,7 +181,7 @@ LINQ to XML provides support for through LINQ to XML handles white space more simply than the DOM. -A common scenario is to read indented XML, create an in-memory XML tree without any white-space text nodes (that is, not preserving white space), do some operations on the XML, and then save the XML with indentation. When you serialize the XML with formatting, only significant white space in the XML tree is preserved. This is the default behavior for LINQ to XML. +A common scenario is to read indented XML, create an in-memory XML tree without any white space text nodes (that is, not preserving white space), do some operations on the XML, and then save the XML with indentation. When you serialize the XML with formatting, only significant white space in the XML tree is preserved. This is the default behavior for LINQ to XML. Another common scenario is to read and modify XML that has already been intentionally indented. You might not want to change this indentation in any way. In LINQ to XML, you can do this by: diff --git a/docs/standard/linq/mixed-declarative-imperative-code-bugs.md b/docs/standard/linq/mixed-declarative-imperative-code-bugs.md new file mode 100644 index 0000000000000..0b0d7523918ad --- /dev/null +++ b/docs/standard/linq/mixed-declarative-imperative-code-bugs.md @@ -0,0 +1,247 @@ +--- +title: Mixed declarative/imperative code bugs - LINQ to XML +description: Learn how to recognize and avoid problems that can occur when code iterates along an axis making changes. +ms.date: 07/20/2015 +dev_langs: + - "csharp" + - "vb" +ms.assetid: fada62d0-0680-4e73-945a-2b00d7a507af +--- + +# Mixed declarative/imperative code bugs (LINQ to XML) + +LINQ to XML contains various methods that allow you to modify an XML tree directly. You can add elements, delete elements, change the contents of an element, add attributes, and so on. This programming interface is described in [Modify XML trees](in-memory-xml-tree-modification-vs-functional-construction.md). If you're iterating through one of the axes, such as , and you're modifying the XML tree as you iterate through the axis, you can end up with some strange bugs. + +This problem is sometimes known as "The Halloween Problem". + +When you write some code using LINQ that iterates through a collection, you're writing code in a declarative style. It's more akin to describing *what* you want, rather that *how* you want to get it done. If you write code that 1) gets the first element, 2) tests it for some condition, 3) modifies it, and 4) puts it back into the list, then this would be imperative code. You're telling the computer *how* to do what you want done. + +Mixing these styles of code in the same operation is what leads to problems. Consider the following: + +Suppose you have a linked list with three items in it (a, b, and c): + +> a -> b -> c + +Now, suppose that you want to move through the linked list, adding three new items (a', b', and c'). You want the resulting linked list to look like this: + + > a -> a' -> b -> b' -> c -> c' + +So you write code that iterates through the list, and for every item, adds a new item right after it. What happens is that your code will first see the `a` element, and insert `a'` after it. Now, your code will move to the next node in the list, which is now `a'`, so it adds a new item between a' and b to the list! + +How would you solve this? Well, you might make a copy of the original linked list, and create a completely new list. Or if you're writing purely imperative code, you might find the first item, add the new item, and then advance twice in the linked list, advancing over the element that you just added. + +## Example: Adding while iterating + +For example, suppose you want to write code to create a duplicate of every element in a tree: + +```csharp +XElement root = new XElement("Root", + new XElement("A", "1"), + new XElement("B", "2"), + new XElement("C", "3") +); +foreach (XElement e in root.Elements()) + root.Add(new XElement(e.Name, (string)e)); +``` + +```vb +Dim root As XElement = _ + + 1 + 2 + 3 + +For Each e As XElement In root.Elements() + root.Add(New XElement(e.Name, e.Value)) +Next +``` + +This code goes into an infinite loop. The `foreach` statement iterates through the `Elements()` axis, adding new elements to the `doc` element. It ends up iterating also through the elements it just added. And because it allocates new objects with every iteration of the loop, it will eventually consume all available memory. + +You can fix this problem by pulling the collection into memory using the standard query operator, as follows: + +```csharp +XElement root = new XElement("Root", + new XElement("A", "1"), + new XElement("B", "2"), + new XElement("C", "3") +); +foreach (XElement e in root.Elements().ToList()) + root.Add(new XElement(e.Name, (string)e)); +Console.WriteLine(root); +``` + +```vb +Dim root As XElement = _ + + 1 + 2 + 3 + +For Each e As XElement In root.Elements().ToList() + root.Add(New XElement(e.Name, e.Value)) +Next +Console.WriteLine(root) +``` + +Now the code works. The resulting XML tree is the following: + +```xml + + 1 + 2 + 3 + 1 + 2 + 3 + +``` + +## Example: Deleting while iterating + +If you want to delete all nodes at a certain level, you might be tempted to write code like the following: + +```csharp +XElement root = new XElement("Root", + new XElement("A", "1"), + new XElement("B", "2"), + new XElement("C", "3") +); +foreach (XElement e in root.Elements()) + e.Remove(); +Console.WriteLine(root); +``` + +```vb +Dim root As XElement = _ + + 1 + 2 + 3 + +For Each e As XElement In root.Elements() + e.Remove() +Next +Console.WriteLine(root) +``` + +However, this doesn't do what you want. In this situation, after you have removed the first element, A, it's removed from the XML tree contained in root, and the code in the Elements method that does the iterating cannot find the next element. + +This example produces the following output: + +```xml + + 2 + 3 + +``` + +The solution again is to call to materialize the collection, as follows: + +```csharp +XElement root = new XElement("Root", + new XElement("A", "1"), + new XElement("B", "2"), + new XElement("C", "3") +); +foreach (XElement e in root.Elements().ToList()) + e.Remove(); +Console.WriteLine(root); +``` + +```vb +Dim root As XElement = _ + + 1 + 2 + 3 + +For Each e As XElement In root.Elements().ToList() + e.Remove() +Next +Console.WriteLine(root) +``` + +This example produces the following output: + +```xml + +``` + +Alternatively, you can eliminate the iteration altogether by calling on the parent element: + +```csharp +XElement root = new XElement("Root", + new XElement("A", "1"), + new XElement("B", "2"), + new XElement("C", "3") +); +root.RemoveAll(); +Console.WriteLine(root); +``` + +```vb +Dim root As XElement = _ + + 1 + 2 + 3 + +root.RemoveAll() +Console.WriteLine(root) +``` + +## Example: Why LINQ can't automatically handle these issues + +One approach would be to always bring everything into memory instead of doing lazy evaluation. However, it would be very expensive in terms of performance and memory use. In fact, if LINQ, and LINQ to XML, were to take this approach, it would fail in real-world situations. + +Another possible approach would be to put some sort of transaction syntax into LINQ, and have the compiler attempt to analyze the code to determine if any particular collection needed to be materialized. However, attempting to determine all code that has side-effects is incredibly complex. Consider the following code: + +```csharp +var z = + from e in root.Elements() + where TestSomeCondition(e) + select DoMyProjection(e); +``` + +```vb +Dim z = _ + From e In root.Elements() _ + Where (TestSomeCondition(e)) _ + Select DoMyProjection(e) +``` + +Such analysis code would need to analyze the methods TestSomeCondition and DoMyProjection, and all methods that those methods called, to determine if any code had side-effects. But the analysis code could not just look for any code that had side-effects. It would need to select for just the code that had side-effects on the child elements of `root` in this situation. + +LINQ to XML doesn't attempt to do any such analysis. It's up to you to avoid these problems. + +## Example: Use declarative code to generate a new XML tree rather than modify the existing tree + +To avoid such problems, don't mix declarative and imperative code, even if you know exactly the semantics of your collections and the semantics of the methods that modify the XML tree. If you write code that avoids problems, your code will need to be maintained by other developers in the future, and they may not be as clear on the issues. If you mix declarative and imperative coding styles, your code will be more brittle. If you write code that materializes a collection so that these problems are avoided, note it with comments as appropriate in your code, so that maintenance programmers will understand the issue. + +If performance and other considerations allow, use only declarative code. Don't modify your existing XML tree. Instead, generate a new one as shown in the following example: + +```csharp +XElement root = new XElement("Root", + new XElement("A", "1"), + new XElement("B", "2"), + new XElement("C", "3") +); +XElement newRoot = new XElement("Root", + root.Elements(), + root.Elements() +); +Console.WriteLine(newRoot); +``` + +```vb +Dim root As XElement = _ + + 1 + 2 + 3 + +Dim newRoot As XElement = New XElement("Root", _ + root.Elements(), root.Elements()) +Console.WriteLine(newRoot) +``` diff --git a/docs/standard/linq/preserve-white-space-loading-parsing-xml.md b/docs/standard/linq/preserve-white-space-loading-parsing-xml.md index c78ea64fa847e..0057b30375fbe 100644 --- a/docs/standard/linq/preserve-white-space-loading-parsing-xml.md +++ b/docs/standard/linq/preserve-white-space-loading-parsing-xml.md @@ -1,19 +1,19 @@ --- title: Preserve white space while loading or parsing XML - LINQ to XML -description: You can control the white-space behavior of LINQ to XML methods that populate XML trees. For instance, you can remove indentation for in-memory processing, or leave it as is. +description: You can control the white space behavior of LINQ to XML methods that populate XML trees. For instance, you can remove indentation for in-memory processing, or leave it as is. ms.date: 07/20/2015 ms.assetid: f3ff58c4-55aa-4fcd-b933-e3a2ee6e706c --- # Preserve white space while loading or parsing XML (LINQ to XML) -This article describes how to control the white-space behavior of LINQ to XML. +This article describes how to control the white space behavior of LINQ to XML. A common scenario is to read indented XML, create an in-memory XML tree without any white space text nodes (that is, not preserving white space), do some operations on the XML, and then save the XML with indentation. When you serialize the XML with formatting, only significant white space in the XML tree is preserved. This is the default behavior for LINQ to XML. Another common scenario is to read and modify XML that has already been intentionally indented. You might not want to change this indentation in any way. To do this in LINQ to XML, you preserve white space when you load or parse the XML and disable formatting when you serialize the XML. -This article describes the white-space behavior of methods that populate XML trees. For information about controlling white space when you serialize XML trees, see [Preserve white space while serializing](preserve-white-space-serializing.md). +This article describes the white space behavior of methods that populate XML trees. For information about controlling white space when you serialize XML trees, see [Preserve white space while serializing](preserve-white-space-serializing.md). ## Behavior of methods that populate XML trees diff --git a/docs/standard/linq/program-nodes.md b/docs/standard/linq/program-nodes.md new file mode 100644 index 0000000000000..bfb0fe4a031d3 --- /dev/null +++ b/docs/standard/linq/program-nodes.md @@ -0,0 +1,251 @@ +--- +title: Programming with nodes - LINQ to XML +description: Learn how to code at the node level of an XML tree. +ms.date: 07/20/2015 +dev_langs: + - "csharp" + - "vb" +ms.assetid: c38df0f2-c805-431a-93ff-9103a4284c2f +--- + +# Programming with nodes (LINQ to XML) + +LINQ to XML developers who need to write programs such as an XML editor, a transform system, or a report writer often need code that works at a finer level of granularity than elements and attributes. They often need to work at the node level, manipulating text nodes, processing instructions, and processing comments. This topic provides information about programming at the node level. + +## Example: The `Parent` property values of the child nodes of XDocument are set to `null` + +The property contains the parent , not the parent node. Child nodes of have no parent . Their parent is the document, so the property for those nodes is set to `null`. + +The following example demonstrates this: + +```csharp +XDocument doc = XDocument.Parse(@""); +Console.WriteLine(doc.Nodes().OfType().First().Parent == null); +Console.WriteLine(doc.Root.Parent == null); +``` + +```vb +Dim doc As XDocument = XDocument.Parse("") +Console.WriteLine(doc.Nodes().OfType(Of XComment).First().Parent Is Nothing) +Console.WriteLine(doc.Root.Parent Is Nothing) +``` + +This example produces the following output: + +```output +True +True +``` + +## Example: Adding text may or may not create a new text node + +In a number of XML programming models, adjacent text nodes are always merged. This is sometimes called normalization of text nodes. LINQ to XML doesn't normalize text nodes. If you add two text nodes to the same element, it will result in adjacent text nodes. However, if you add content specified as a string rather than as an node, LINQ to XML might merge the string with an adjacent text node. The following example demonstrates this. + +```csharp +XElement xmlTree = new XElement("Root", "Content"); + +Console.WriteLine(xmlTree.Nodes().OfType().Count()); + +// this doesn't add a new text node +xmlTree.Add("new content"); +Console.WriteLine(xmlTree.Nodes().OfType().Count()); + +// this does add a new, adjacent text node +xmlTree.Add(new XText("more text")); +Console.WriteLine(xmlTree.Nodes().OfType().Count()); +``` + +```vb +Dim xmlTree As XElement = Content +Console.WriteLine(xmlTree.Nodes().OfType(Of XText)().Count()) + +' This doesn't add a new text node. +xmlTree.Add("new content") +Console.WriteLine(xmlTree.Nodes().OfType(Of XText)().Count()) + +'// This does add a new, adjacent text node. +xmlTree.Add(New XText("more text")) +Console.WriteLine(xmlTree.Nodes().OfType(Of XText)().Count()) +``` + + This example produces the following output: + +```output +1 +1 +2 +``` + +## Example: Setting a text node value to the empty string doesn't delete the node + +In some XML programming models, text nodes are guaranteed to not contain the empty string. The reasoning is that such a text node has no impact on serialization of the XML. However, for the same reason that adjacent text nodes are possible, if you remove the text from a text node by setting its value to the empty string, the text node itself won't be deleted. + +```csharp +XElement xmlTree = new XElement("Root", "Content"); +XText textNode = xmlTree.Nodes().OfType().First(); + +// the following line doesn't cause the removal of the text node. +textNode.Value = ""; + +XText textNode2 = xmlTree.Nodes().OfType().First(); +Console.WriteLine(">>{0}<<", textNode2); +``` + +```vb +Dim xmlTree As XElement = Content +Dim textNode As XText = xmlTree.Nodes().OfType(Of XText)().First() + +' The following line doesn't cause the removal of the text node. +textNode.Value = "" + +Dim textNode2 As XText = xmlTree.Nodes().OfType(Of XText)().First() +Console.WriteLine(">>{0}<<", textNode2) +``` + +This example produces the following output: + +```output +>><< +``` + +## Example: An element with one empty text node is serialized differently from one with no text node + +If an element contains only a child text node that's empty, it's serialized with the long tag syntax: ``. If an element contains no child nodes whatsoever, it's serialized with the short tag syntax: ``. + +```csharp +XElement child1 = new XElement("Child1", + new XText("") +); +XElement child2 = new XElement("Child2"); +Console.WriteLine(child1); +Console.WriteLine(child2); +``` + +```vb +Dim child1 As XElement = New XElement("Child1", _ + New XText("") _ +) +Dim child2 As XElement = New XElement("Child2") +Console.WriteLine(child1) +Console.WriteLine(child2) +``` + +This example produces the following output: + +```xml + + +``` + +## Example: Namespaces are attributes in the LINQ to XML tree + +Even though namespace declarations have identical syntax to attributes, in some programming interfaces, such as XSLT and XPath, namespace declarations aren't considered to be attributes. However, in LINQ to XML, namespaces are stored as objects in the XML tree. If you iterate through the attributes of an element that contains a namespace declaration, the namespace declaration is one of the items in the returned collection. The property indicates whether an attribute is a namespace declaration. + +```csharp +XElement root = XElement.Parse( +@""); +foreach (XAttribute att in root.Attributes()) + Console.WriteLine("{0} IsNamespaceDeclaration:{1}", att, att.IsNamespaceDeclaration); +``` + +```vb +Dim root As XElement = _ + +For Each att As XAttribute In root.Attributes() + Console.WriteLine("{0} IsNamespaceDeclaration:{1}", att, _ + att.IsNamespaceDeclaration) +Next +``` + +This example produces the following output: + +```output +xmlns="http://www.adventure-works.com" IsNamespaceDeclaration:True +xmlns:fc="www.fourthcoffee.com" IsNamespaceDeclaration:True +AnAttribute="abc" IsNamespaceDeclaration:False +``` + +## Example: XPath axis methods don't return the child text nodes of XDocument + +LINQ to XML allows for child text nodes of an , as long as the text nodes contain only white space. However, the XPath object model doesn't include white space as child nodes of a document, so when you iterate through the children of an using the axis, white space text nodes will be returned. However, when you iterate through the children of an using the XPath axis methods, white space text nodes won't be returned. + +```csharp +// Create a document with some white space child nodes of the document. +XDocument root = XDocument.Parse( +@" + + + + +", LoadOptions.PreserveWhitespace); + +// count the white space child nodes using LINQ to XML +Console.WriteLine(root.Nodes().OfType().Count()); + +// count the white space child nodes using XPathEvaluate +Console.WriteLine(((IEnumerable)root.XPathEvaluate("text()")).OfType().Count()); +``` + +```vb +' Create a document with some white space child nodes of the document. +Dim root As XDocument = XDocument.Parse( _ +"" & _ +vbNewLine & "" & vbNewLine & "" & vbNewLine, _ +LoadOptions.PreserveWhitespace) + +' Count the white space child nodes using LINQ to XML. +Console.WriteLine(root.Nodes().OfType(Of XText)().Count()) + +' Count the white space child nodes using XPathEvaluate. +Dim nodes As IEnumerable = CType(root.XPathEvaluate("text()"), IEnumerable) +Console.WriteLine(nodes.OfType(Of XText)().Count()) +``` + +This example produces the following output: + +```output +3 +0 +``` + +### The XML declaration node of an XDocument is a property, not a child node + +When you iterate through the child nodes of an , you won't see the XML declaration object. It's a property of the document, not a child node of it. + +```csharp +XDocument doc = new XDocument( + new XDeclaration("1.0", "utf-8", "yes"), + new XElement("Root") +); +doc.Save("Temp.xml"); +Console.WriteLine(File.ReadAllText("Temp.xml")); + +// this shows that there is only one child node of the document +Console.WriteLine(doc.Nodes().Count()); +``` + +```vb +Dim doc As XDocument = _ + + + +doc.Save("Temp.xml") +Console.WriteLine(File.ReadAllText("Temp.xml")) + +' This shows that there is only one child node of the document. +Console.WriteLine(doc.Nodes().Count()) +``` + +This example produces the following output: + +```output + + +1 +``` diff --git a/docs/standard/linq/stream-xml-fragments-access-header-information.md b/docs/standard/linq/stream-xml-fragments-access-header-information.md new file mode 100644 index 0000000000000..d365ea92db2bd --- /dev/null +++ b/docs/standard/linq/stream-xml-fragments-access-header-information.md @@ -0,0 +1,299 @@ +--- +title: How to stream XML fragments with access to header information - LINQ to XML +description: Learn how to implement and use a custom axis method that streams XML fragments from the file specified by a URI. +ms.date: 07/20/2015 +dev_langs: + - "csharp" + - "vb" +ms.assetid: 7f242770-b0c7-418d-894b-643215e1f8aa +--- + +# How to stream XML fragments with access to header information (LINQ to XML) + +Sometimes you have to read arbitrarily large XML files, and write your application so that the memory footprint of the application is predictable. If you attempt to populate an XML tree with a large XML file, your memory usage will be proportional to the size of the file—that is, excessive. Therefore, you should use a streaming technique instead. + +One option is to write your application using . However, you might want to use LINQ to query the XML tree. If so, you can write your own custom axis method. For more information, see [How to write a LINQ to XML axis method](write-linq-xml-axis-method.md). + +To write your own axis method, you write a small method that uses the to read nodes until it reaches one of the nodes in which you're interested. The method then calls , which reads from the and instantiates an XML fragment. It then yields each fragment through `yield return` to the method that's enumerating your custom axis method. You can then write LINQ queries on your custom axis method. + +Streaming techniques are best applied in situations where you need to process the source document only once, and you can process the elements in document order. Certain standard query operators, such as , iterate their source, collect all of the data, sort it, and then finally yield the first item in the sequence. If you use a query operator that materializes its source before yielding the first item, you won't retain a small memory footprint. + +## Example: Implement and use a custom axis method that streams XML fragments from the file specified by a URI + +Sometimes the problem gets just a little more interesting. In the following XML document, the consumer of your custom axis method also has to know the name of the customer that each item belongs to. + +```xml + + + + A. Datum Corporation + + 0001 + + + 0002 + + + 0003 + + + 0004 + + + + Fabrikam, Inc. + + 0005 + + + 0006 + + + 0007 + + + 0008 + + + + Southridge Video + + 0009 + + + 0010 + + + +``` + +The approach that this example takes is to also watch for this header information, save the header information, and then build a small XML tree that contains both the header information and the detail that you're enumerating. The axis method then yields this new, small XML tree. The query then has access to the header information as well as the detail information. + +This approach has a small memory footprint. As each detail XML fragment is yielded, no references are kept to the previous fragment, and it's available for garbage collection. This technique creates many short lived objects on the heap. + +The following example shows how to implement and use a custom axis method that streams XML fragments from the file specified by the URI. This custom axis is written such that it expects a document that has `Customer`, `Name`, and `Item` elements, and that those elements will be arranged as in the above `Source.xml` document. It's a simplistic implementation. A more robust implementation would be prepared to parse an invalid document. + +```csharp +static IEnumerable StreamCustomerItem(string uri) +{ + using (XmlReader reader = XmlReader.Create(uri)) + { + XElement name = null; + XElement item = null; + + reader.MoveToContent(); + + // Parse the file, save header information when encountered, and yield the + // Item XElement objects as they are created. + + // loop through Customer elements + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element + && reader.Name == "Customer") + { + // move to Name element + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element && + reader.Name == "Name") + { + name = XElement.ReadFrom(reader) as XElement; + break; + } + } + + // loop through Item elements + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.EndElement) + break; + if (reader.NodeType == XmlNodeType.Element + && reader.Name == "Item") + { + item = XElement.ReadFrom(reader) as XElement; + if (item != null) { + XElement tempRoot = new XElement("Root", + new XElement(name) + ); + tempRoot.Add(item); + yield return item; + } + } + } + } + } + } +} + +static void Main(string[] args) +{ + XElement xmlTree = new XElement("Root", + from el in StreamCustomerItem("Source.xml") + where (int)el.Element("Key") >= 3 && (int)el.Element("Key") <= 7 + select new XElement("Item", + new XElement("Customer", (string)el.Parent.Element("Name")), + new XElement(el.Element("Key")) + ) + ); + Console.WriteLine(xmlTree); +} +``` + +```vb +Module Module1 + + Sub Main() + Dim xmlTree = + <%= + From el In New StreamCustomerItem("Source.xml") + Let itemKey = CInt(el..Value) + Where itemKey >= 3 AndAlso itemKey <= 7 + Select + <%= el.Parent..Value %> + <%= el. %> + + %> + + + Console.WriteLine(xmlTree) + End Sub + +End Module + +Public Class StreamCustomerItem + Implements IEnumerable(Of XElement) + + Private _uri As String + + Public Sub New(ByVal uri As String) + _uri = uri + End Sub + + Public Function GetEnumerator() As IEnumerator(Of XElement) Implements IEnumerable(Of XElement).GetEnumerator + Return New StreamCustomerItemEnumerator(_uri) + End Function + + Public Function GetEnumerator1() As IEnumerator Implements IEnumerable.GetEnumerator + Return Me.GetEnumerator() + End Function +End Class + +Public Class StreamCustomerItemEnumerator + Implements IEnumerator(Of XElement) + + Private _current As XElement + Private _customerName As String + Private _reader As Xml.XmlReader + Private _uri As String + + Public Sub New(ByVal uri As String) + _uri = uri + _reader = Xml.XmlReader.Create(_uri) + _reader.MoveToContent() + End Sub + + Public ReadOnly Property Current As XElement Implements IEnumerator(Of XElement).Current + Get + Return _current + End Get + End Property + + Public ReadOnly Property Current1 As Object Implements IEnumerator.Current + Get + Return Me.Current + End Get + End Property + + Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext + Dim item As XElement + Dim name As XElement + + ' Parse the file, save header information when encountered, and return the + ' current Item XElement. + + ' loop through Customer elements + While _reader.Read() + If _reader.NodeType = Xml.XmlNodeType.Element Then + Select Case _reader.Name + Case "Customer" + ' move to Name element + While _reader.Read() + + If _reader.NodeType = Xml.XmlNodeType.Element AndAlso + _reader.Name = "Name" Then + + name = TryCast(XElement.ReadFrom(_reader), XElement) + _customerName = If(name IsNot Nothing, name.Value, "") + Exit While + End If + + End While + Case "Item" + item = TryCast(XElement.ReadFrom(_reader), XElement) + Dim tempRoot = + <%= _customerName %> + <%= item %> + + _current = item + Return True + End Select + End If + End While + + Return False + End Function + + Public Sub Reset() Implements IEnumerator.Reset + _reader = Xml.XmlReader.Create(_uri) + _reader.MoveToContent() + End Sub + +#Region "IDisposable Support" + Private disposedValue As Boolean ' To detect redundant calls + + ' IDisposable + Protected Overridable Sub Dispose(ByVal disposing As Boolean) + If Not Me.disposedValue Then + If disposing Then + _reader.Close() + End If + End If + Me.disposedValue = True + End Sub + + Public Sub Dispose() Implements IDisposable.Dispose + Dispose(True) + GC.SuppressFinalize(Me) + End Sub +#End Region + +End Class +``` + + This code produces the following output: + +```xml + + + A. Datum Corporation + 0003 + + + A. Datum Corporation + 0004 + + + Fabrikam, Inc. + 0005 + + + Fabrikam, Inc. + 0006 + + + Fabrikam, Inc. + 0007 + + +``` diff --git a/docs/standard/linq/xml-literals.md b/docs/standard/linq/xml-literals.md index af1c05de2a18f..2acee947c63c2 100644 --- a/docs/standard/linq/xml-literals.md +++ b/docs/standard/linq/xml-literals.md @@ -172,7 +172,7 @@ Dim xmlTree1 As XElement = _ 1 -' Create an element that isn't parented. +' Create an element that's not parented. Dim child2 As XElement = 2 ' Create a tree and add Child1 and Child2 to it. diff --git a/docs/visual-basic/programming-guide/concepts/linq/how-to-stream-xml-fragments-with-access-to-header-information.md b/docs/visual-basic/programming-guide/concepts/linq/how-to-stream-xml-fragments-with-access-to-header-information.md deleted file mode 100644 index 1c91ecd27376a..0000000000000 --- a/docs/visual-basic/programming-guide/concepts/linq/how-to-stream-xml-fragments-with-access-to-header-information.md +++ /dev/null @@ -1,229 +0,0 @@ ---- -title: "How to: Stream XML Fragments with Access to Header Information" -ms.date: 07/20/2015 -ms.assetid: effd10df-87c4-4d7a-8a9a-1434d829dca5 ---- -# How to: Stream XML Fragments with Access to Header Information (Visual Basic) -Sometimes you have to read arbitrarily large XML files, and write your application so that the memory footprint of the application is predictable. If you attempt to populate an XML tree with a large XML file, your memory usage will be proportional to the size of the file—that is, excessive. Therefore, you should use a streaming technique instead. - - One option is to write your application using . However, you might want to use LINQ to query the XML tree. If this is the case, you can write your own custom axis method. For more information, see [How to: Write a LINQ to XML Axis Method (Visual Basic)](how-to-write-a-linq-to-xml-axis-method.md). - - To write your own axis method, you write a small method that uses the to read nodes until it reaches one of the nodes in which you are interested. The method then calls , which reads from the and instantiates an XML fragment. You can then write LINQ queries on your custom axis method. - - Streaming techniques are best applied in situations where you need to process the source document only once, and you can process the elements in document order. Certain standard query operators, such as , iterate their source, collect all of the data, sort it, and then finally yield the first item in the sequence. Note that if you use a query operator that materializes its source before yielding the first item, you will not retain a small memory footprint. - -## Example - Sometimes the problem gets just a little more interesting. In the following XML document, the consumer of your custom axis method also has to know the name of the customer that each item belongs to. - -```xml - - - - A. Datum Corporation - - 0001 - - - 0002 - - - 0003 - - - 0004 - - - - Fabrikam, Inc. - - 0005 - - - 0006 - - - 0007 - - - 0008 - - - - Southridge Video - - 0009 - - - 0010 - - - -``` - - The approach that this example takes is to also watch for this header information, save the header information, and then build a small XML tree that contains both the header information and the detail that you are enumerating. The axis method then yields this new, small XML tree. The query then has access to the header information as well as the detail information. - - This approach has a small memory footprint. As each detail XML fragment is yielded, no references are kept to the previous fragment, and it is available for garbage collection. Note that this technique creates many short lived objects on the heap. - - The following example shows how to implement and use a custom axis method that streams XML fragments from the file specified by the URI. This custom axis is specifically written such that it expects a document that has `Customer`, `Name`, and `Item` elements, and that those elements will be arranged as in the above `Source.xml` document. It is a simplistic implementation. A more robust implementation would be prepared to parse an invalid document. - -```vb -Module Module1 - - Sub Main() - Dim xmlTree = - <%= - From el In New StreamCustomerItem("Source.xml") - Let itemKey = CInt(el..Value) - Where itemKey >= 3 AndAlso itemKey <= 7 - Select - <%= el.Parent..Value %> - <%= el. %> - - %> - - - Console.WriteLine(xmlTree) - End Sub - -End Module - -Public Class StreamCustomerItem - Implements IEnumerable(Of XElement) - - Private _uri As String - - Public Sub New(ByVal uri As String) - _uri = uri - End Sub - - Public Function GetEnumerator() As IEnumerator(Of XElement) Implements IEnumerable(Of XElement).GetEnumerator - Return New StreamCustomerItemEnumerator(_uri) - End Function - - Public Function GetEnumerator1() As IEnumerator Implements IEnumerable.GetEnumerator - Return Me.GetEnumerator() - End Function -End Class - -Public Class StreamCustomerItemEnumerator - Implements IEnumerator(Of XElement) - - Private _current As XElement - Private _customerName As String - Private _reader As Xml.XmlReader - Private _uri As String - - Public Sub New(ByVal uri As String) - _uri = uri - _reader = Xml.XmlReader.Create(_uri) - _reader.MoveToContent() - End Sub - - Public ReadOnly Property Current As XElement Implements IEnumerator(Of XElement).Current - Get - Return _current - End Get - End Property - - Public ReadOnly Property Current1 As Object Implements IEnumerator.Current - Get - Return Me.Current - End Get - End Property - - Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext - Dim item As XElement - Dim name As XElement - - ' Parse the file, save header information when encountered, and return the - ' current Item XElement. - - ' loop through Customer elements - While _reader.Read() - If _reader.NodeType = Xml.XmlNodeType.Element Then - Select Case _reader.Name - Case "Customer" - ' move to Name element - While _reader.Read() - - If _reader.NodeType = Xml.XmlNodeType.Element AndAlso - _reader.Name = "Name" Then - - name = TryCast(XElement.ReadFrom(_reader), XElement) - _customerName = If(name IsNot Nothing, name.Value, "") - Exit While - End If - - End While - Case "Item" - item = TryCast(XElement.ReadFrom(_reader), XElement) - Dim tempRoot = - <%= _customerName %> - <%= item %> - - _current = item - Return True - End Select - End If - End While - - Return False - End Function - - Public Sub Reset() Implements IEnumerator.Reset - _reader = Xml.XmlReader.Create(_uri) - _reader.MoveToContent() - End Sub - -#Region "IDisposable Support" - Private disposedValue As Boolean ' To detect redundant calls - - ' IDisposable - Protected Overridable Sub Dispose(ByVal disposing As Boolean) - If Not Me.disposedValue Then - If disposing Then - _reader.Close() - End If - End If - Me.disposedValue = True - End Sub - - Public Sub Dispose() Implements IDisposable.Dispose - Dispose(True) - GC.SuppressFinalize(Me) - End Sub -#End Region - -End Class -``` - - This code produces the following output: - -```xml - - - A. Datum Corporation - 0003 - - - A. Datum Corporation - 0004 - - - Fabrikam, Inc. - 0005 - - - Fabrikam, Inc. - 0006 - - - Fabrikam, Inc. - 0007 - - -``` - -## See also - -- [Advanced LINQ to XML Programming (Visual Basic)](advanced-linq-to-xml-programming.md) diff --git a/docs/visual-basic/programming-guide/concepts/linq/linq-to-xml-events.md b/docs/visual-basic/programming-guide/concepts/linq/linq-to-xml-events.md deleted file mode 100644 index 4c39082b4f13b..0000000000000 --- a/docs/visual-basic/programming-guide/concepts/linq/linq-to-xml-events.md +++ /dev/null @@ -1,113 +0,0 @@ ---- -title: "LINQ to XML Events" -ms.date: 07/20/2015 -ms.assetid: 34923928-b99c-4004-956e-38f6db25e910 ---- -# LINQ to XML Events (Visual Basic) -[!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)] events enable you to be notified when an XML tree is altered. - - You can add events to an instance of any . The event handler will then receive events for modifications to that and any of its descendants. For example, you can add an event handler to the root of the tree, and handle all modifications to the tree from that event handler. - - For examples of [!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)] events, see and . - -## Types and Events - You use the following types when working with events: - -|Type|Description| -|----------|-----------------| -||Specifies the event type when an event is raised for an .| -||Provides data for the and events.| - - The following events are raised when you modify an XML tree: - -|Event|Description| -|-----------|-----------------| -||Occurs just before this or any of its descendants is going to change.| -||Occurs when an has changed or any of its descendants have changed.| - -## Example - -### Description - Events are useful when you want to maintain some aggregate information in an XML tree. For example, you may want maintain an invoice total that is the sum of the line items of the invoice. This example uses events to maintain the total of all of the child elements under the complex element `Items`. - -### Code - -```vb -Module Module1 - Dim WithEvents items As XElement = Nothing - Dim total As XElement = Nothing - Dim root As XElement = _ - - 0 - - - - Private Sub XObjectChanged( _ - ByVal sender As Object, _ - ByVal cea As XObjectChangeEventArgs) _ - Handles items.Changed - Select Case cea.ObjectChange - Case XObjectChange.Add - If sender.GetType() Is GetType(XElement) Then - total.Value = CStr(CInt(total.Value) + _ - CInt((DirectCast(sender, XElement)).Value)) - End If - If sender.GetType() Is GetType(XText) Then - total.Value = CStr(CInt(total.Value) + _ - CInt((DirectCast(sender, XText)).Value)) - End If - Case XObjectChange.Remove - If sender.GetType() Is GetType(XElement) Then - total.Value = CStr(CInt(total.Value) - _ - CInt((DirectCast(sender, XElement)).Value)) - End If - If sender.GetType() Is GetType(XText) Then - total.Value = CStr(CInt(total.Value) - _ - CInt((DirectCast(sender, XText)).Value)) - End If - End Select - Console.WriteLine("Changed {0} {1}", _ - sender.GetType().ToString(), _ - cea.ObjectChange.ToString()) - End Sub - - Sub Main() - total = root.(0) - items = root.(0) - items.SetElementValue("Item1", 25) - items.SetElementValue("Item2", 50) - items.SetElementValue("Item2", 75) - items.SetElementValue("Item3", 133) - items.SetElementValue("Item1", Nothing) - items.SetElementValue("Item4", 100) - Console.WriteLine("Total:{0}", CInt(total)) - Console.WriteLine(root) - End Sub -End Module -``` - -### Comments - This code produces the following output: - -```console -Changed System.Xml.Linq.XElement Add -Changed System.Xml.Linq.XElement Add -Changed System.Xml.Linq.XText Remove -Changed System.Xml.Linq.XText Add -Changed System.Xml.Linq.XElement Add -Changed System.Xml.Linq.XElement Remove -Changed System.Xml.Linq.XElement Add -Total:308 - - 308 - - 75 - 133 - 100 - - -``` - -## See also - -- [Advanced LINQ to XML Programming (Visual Basic)](advanced-linq-to-xml-programming.md) diff --git a/docs/visual-basic/programming-guide/concepts/linq/mixed-declarative-code-imperative-code-bugs-linq-to-xml.md b/docs/visual-basic/programming-guide/concepts/linq/mixed-declarative-code-imperative-code-bugs-linq-to-xml.md deleted file mode 100644 index bb652f692bb45..0000000000000 --- a/docs/visual-basic/programming-guide/concepts/linq/mixed-declarative-code-imperative-code-bugs-linq-to-xml.md +++ /dev/null @@ -1,175 +0,0 @@ ---- -title: "Mixed Declarative Code-Imperative Code Bugs (LINQ to XML)" -ms.date: 07/20/2015 -ms.assetid: f12b1ab4-bb92-4b92-a648-0525e45b3ce7 ---- -# Mixed Declarative Code/Imperative Code Bugs (LINQ to XML) (Visual Basic) -[!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)] contains various methods that allow you to modify an XML tree directly. You can add elements, delete elements, change the contents of an element, add attributes, and so on. This programming interface is described in [Modifying XML Trees (LINQ to XML) (Visual Basic)](modifying-xml-trees-linq-to-xml.md). If you are iterating through one of the axes, such as , and you are modifying the XML tree as you iterate through the axis, you can end up with some strange bugs. - - This problem is sometimes known as "The Halloween Problem". - -## Definition of the Problem - When you write some code using LINQ that iterates through a collection, you are writing code in a declarative style. It is more akin to describing *what* you want, rather that *how* you want to get it done. If you write code that 1) gets the first element, 2) tests it for some condition, 3) modifies it, and 4) puts it back into the list, then this would be imperative code. You are telling the computer *how* to do what you want done. - - Mixing these styles of code in the same operation is what leads to problems. Consider the following: - - Suppose you have a linked list with three items in it (a, b, and c): - - `a -> b -> c` - - Now, suppose that you want to move through the linked list, adding three new items (a', b', and c'). You want the resulting linked list to look like this: - - `a -> a' -> b -> b' -> c -> c'` - - So you write code that iterates through the list, and for every item, adds a new item right after it. What happens is that your code will first see the `a` element, and insert `a'` after it. Now, your code will move to the next node in the list, which is now `a'`! It happily adds a new item to the list, `a''`. - - How would you solve this in the real world? Well, you might make a copy of the original linked list, and create a completely new list. Or if you are writing purely imperative code, you might find the first item, add the new item, and then advance twice in the linked list, advancing over the element that you just added. - -## Adding While Iterating - For example, suppose you want to write some code that for every element in a tree, you want to create a duplicate element: - -```vb -Dim root As XElement = _ - - 1 - 2 - 3 - -For Each e As XElement In root.Elements() - root.Add(New XElement(e.Name, e.Value)) -Next -``` - - This code goes into an infinite loop. The `foreach` statement iterates through the `Elements()` axis, adding new elements to the `doc` element. It ends up iterating also through the elements it just added. And because it allocates new objects with every iteration of the loop, it will eventually consume all available memory. - - You can fix this problem by pulling the collection into memory using the standard query operator, as follows: - -```vb -Dim root As XElement = _ - - 1 - 2 - 3 - -For Each e As XElement In root.Elements().ToList() - root.Add(New XElement(e.Name, e.Value)) -Next -Console.WriteLine(root) -``` - - Now the code works. The resulting XML tree is the following: - -```xml - - 1 - 2 - 3 - 1 - 2 - 3 - -``` - -## Deleting While Iterating - If you want to delete all nodes at a certain level, you might be tempted to write code like the following: - -```vb -Dim root As XElement = _ - - 1 - 2 - 3 - -For Each e As XElement In root.Elements() - e.Remove() -Next -Console.WriteLine(root) -``` - - However, this does not do what you want. In this situation, after you have removed the first element, A, it is removed from the XML tree contained in root, and the code in the Elements method that is doing the iterating cannot find the next element. - - The preceding code produces the following output: - -```xml - - 2 - 3 - -``` - - The solution again is to call to materialize the collection, as follows: - -```vb -Dim root As XElement = _ - - 1 - 2 - 3 - -For Each e As XElement In root.Elements().ToList() - e.Remove() -Next -Console.WriteLine(root) -``` - - This produces the following output: - -```xml - -``` - - Alternatively, you can eliminate the iteration altogether by calling on the parent element: - -```vb -Dim root As XElement = _ - - 1 - 2 - 3 - -root.RemoveAll() -Console.WriteLine(root) -``` - -## Why Can't LINQ Automatically Handle This? - One approach would be to always bring everything into memory instead of doing lazy evaluation. However, it would be very expensive in terms of performance and memory use. In fact, if LINQ and (LINQ to XML) were to take this approach, it would fail in real-world situations. - - Another possible approach would be to put in some sort of transaction syntax into LINQ, and have the compiler attempt to analyze the code and determine if any particular collection needed to be materialized. However, attempting to determine all code that has side-effects is incredibly complex. Consider the following code: - -```vb -Dim z = _ - From e In root.Elements() _ - Where (TestSomeCondition(e)) _ - Select DoMyProjection(e) -``` - - Such analysis code would need to analyze the methods TestSomeCondition and DoMyProjection, and all methods that those methods called, to determine if any code had side-effects. But the analysis code could not just look for any code that had side-effects. It would need to select for just the code that had side-effects on the child elements of `root` in this situation. - - LINQ to XML does not attempt to do any such analysis. - - It is up to you to avoid these problems. - -## Guidance - First, do not mix declarative and imperative code. - - Even if you know exactly the semantics of your collections and the semantics of the methods that modify the XML tree, if you write some clever code that avoids these categories of problems, your code will need to be maintained by other developers in the future, and they may not be as clear on the issues. If you mix declarative and imperative coding styles, your code will be more brittle. - - If you write code that materializes a collection so that these problems are avoided, note it with comments as appropriate in your code, so that maintenance programmers will understand the issue. - - Second, if performance and other considerations allow, use only declarative code. Don't modify your existing XML tree. Generate a new one. - -```vb -Dim root As XElement = _ - - 1 - 2 - 3 - -Dim newRoot As XElement = New XElement("Root", _ - root.Elements(), root.Elements()) -Console.WriteLine(newRoot) -``` - -## See also - -- [Advanced LINQ to XML Programming (Visual Basic)](advanced-linq-to-xml-programming.md) diff --git a/docs/visual-basic/programming-guide/concepts/linq/programming-with-nodes.md b/docs/visual-basic/programming-guide/concepts/linq/programming-with-nodes.md deleted file mode 100644 index fd567660e31c6..0000000000000 --- a/docs/visual-basic/programming-guide/concepts/linq/programming-with-nodes.md +++ /dev/null @@ -1,170 +0,0 @@ ---- -title: "Programming with Nodes" -ms.date: 07/20/2015 -ms.assetid: d8422a9b-dd37-44a3-8aac-2237ed9561e0 ---- -# Programming with Nodes (Visual Basic) -[!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)] developers who need to write programs such as an XML editor, a transform system, or a report writer often need to write programs that work at a finer level of granularity than elements and attributes. They often need to work at the node level, manipulating text nodes, processing instructions, and comments. This topic provides some details about programming at the node level. - -## Node Details - There are a number of details of programming that a programmer working at the node level should know. - -### Parent Property of Children Nodes of XDocument is Set to Null - The property contains the parent , not the parent node. Child nodes of have no parent . Their parent is the document, so the property for those nodes is set to null. - - The following example demonstrates this: - -```vb -Dim doc As XDocument = XDocument.Parse("") -Console.WriteLine(doc.Nodes().OfType(Of XComment).First().Parent Is Nothing) -Console.WriteLine(doc.Root.Parent Is Nothing) -``` - - This example produces the following output: - -```console -True -True -``` - -### Adjacent Text Nodes are Possible - In a number of XML programming models, adjacent text nodes are always merged. This is sometimes called normalization of text nodes. [!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)] does not normalize text nodes. If you add two text nodes to the same element, it will result in adjacent text nodes. However, if you add content specified as a string rather than as an node, [!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)] might merge the string with an adjacent text node. - - The following example demonstrates this: - -```vb -Dim xmlTree As XElement = Content -Console.WriteLine(xmlTree.Nodes().OfType(Of XText)().Count()) - -' This does not add a new text node. -xmlTree.Add("new content") -Console.WriteLine(xmlTree.Nodes().OfType(Of XText)().Count()) - -'// This does add a new, adjacent text node. -xmlTree.Add(New XText("more text")) -Console.WriteLine(xmlTree.Nodes().OfType(Of XText)().Count()) -``` - - This example produces the following output: - -```console -1 -1 -2 -``` - -### Empty Text Nodes are Possible - In some XML programming models, text nodes are guaranteed to not contain the empty string. The reasoning is that such a text node has no impact on serialization of the XML. However, for the same reason that adjacent text nodes are possible, if you remove the text from a text node by setting its value to the empty string, the text node itself will not be deleted. - -```vb -Dim xmlTree As XElement = Content -Dim textNode As XText = xmlTree.Nodes().OfType(Of XText)().First() - -' The following line does not cause the removal of the text node. -textNode.Value = "" - -Dim textNode2 As XText = xmlTree.Nodes().OfType(Of XText)().First() -Console.WriteLine(">>{0}<<", textNode2) -``` - - This example produces the following output: - -```console ->><< -``` - -### An Empty Text Node Impacts Serialization - If an element contains only a child text node that is empty, it is serialized with the long tag syntax: ``. If an element contains no child nodes whatsoever, it is serialized with the short tag syntax: ``. - -```vb -Dim child1 As XElement = New XElement("Child1", _ - New XText("") _ -) -Dim child2 As XElement = New XElement("Child2") -Console.WriteLine(child1) -Console.WriteLine(child2) -``` - - This example produces the following output: - -```xml - - -``` - -### Namespaces are Attributes in the LINQ to XML Tree - Even though namespace declarations have identical syntax to attributes, in some programming interfaces, such as XSLT and XPath, namespace declarations are not considered to be attributes. However, in [!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)], namespaces are stored as objects in the XML tree. If you iterate through the attributes for an element that contains a namespace declaration, you will see the namespace declaration as one of the items in the returned collection. - - The property indicates whether an attribute is a namespace declaration. - -```vb -Dim root As XElement = _ - -For Each att As XAttribute In root.Attributes() - Console.WriteLine("{0} IsNamespaceDeclaration:{1}", att, _ - att.IsNamespaceDeclaration) -Next -``` - - This example produces the following output: - -```console -xmlns="http://www.adventure-works.com" IsNamespaceDeclaration:True -xmlns:fc="www.fourthcoffee.com" IsNamespaceDeclaration:True -AnAttribute="abc" IsNamespaceDeclaration:False -``` - -### XPath Axis Methods Do Not Return Child White Space of XDocument - [!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)] allows for child text nodes of an , as long as the text nodes contain only white space. However, the XPath object model does not include white space as child nodes of a document, so when you iterate through the children of an using the axis, white space text nodes will be returned. However, when you iterate through the children of an using the XPath axis methods, white space text nodes will not be returned. - -```vb -' Create a document with some white space child nodes of the document. -Dim root As XDocument = XDocument.Parse( _ -"" & _ -vbNewLine & "" & vbNewLine & "" & vbNewLine, _ -LoadOptions.PreserveWhitespace) - -' Count the white space child nodes using LINQ to XML. -Console.WriteLine(root.Nodes().OfType(Of XText)().Count()) - -' Count the white space child nodes using XPathEvaluate. -Dim nodes As IEnumerable = CType(root.XPathEvaluate("text()"), IEnumerable) -Console.WriteLine(nodes.OfType(Of XText)().Count()) -``` - - This example produces the following output: - -```console -3 -0 -``` - -### XDeclaration Objects are not Nodes - When you iterate through the children nodes of an , you will not see the XML declaration object. It is a property of the document, not a child node of it. - -```vb -Dim doc As XDocument = _ - - - -doc.Save("Temp.xml") -Console.WriteLine(File.ReadAllText("Temp.xml")) - -' This shows that there is only one child node of the document. -Console.WriteLine(doc.Nodes().Count()) -``` - - This example produces the following output: - -```xml - - -1 -``` - -## See also - -- [Advanced LINQ to XML Programming (Visual Basic)](advanced-linq-to-xml-programming.md)