diff --git a/.openpublishing.redirection.json b/.openpublishing.redirection.json index 2aaf9616fdd68..7b7963b314e77 100644 --- a/.openpublishing.redirection.json +++ b/.openpublishing.redirection.json @@ -2032,6 +2032,26 @@ "redirect_url": "/dotnet/standard/linq/program-nodes", "redirect_document_id": true }, + { + "source_path": "docs/csharp/programming-guide/concepts/linq/how-to-perform-streaming-transform-of-large-xml-documents.md", + "redirect_url": "/dotnet/standard/linq/perform-streaming-transform-large-xml-documents", + "redirect_document_id": true + }, + { + "source_path": "docs/csharp/programming-guide/concepts/linq/how-to-read-and-write-an-encoded-document.md", + "redirect_url": "/dotnet/standard/linq/read-write-encoded-document", + "redirect_document_id": true + }, + { + "source_path": "docs/csharp/programming-guide/concepts/linq/how-to-use-annotations-to-transform-linq-to-xml-trees-in-an-xslt-style.md", + "redirect_url": "/dotnet/standard/linq/use-annotations-transform-linq-xml-trees-xslt-style", + "redirect_document_id": true + }, + { + "source_path": "docs/csharp/programming-guide/concepts/linq/using-xslt-to-transform-an-xml-tree.md", + "redirect_url": "/dotnet/standard/linq/use-xslt-transform-xml-tree", + "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" @@ -5719,6 +5739,26 @@ "redirect_url": "/dotnet/standard/linq/program-nodes", "redirect_document_id": false }, + { + "source_path": "docs/visual-basic/programming-guide/concepts/linq/how-to-perform-streaming-transform-of-large-xml-documents.md", + "redirect_url": "/dotnet/standard/linq/perform-streaming-transform-large-xml-documents", + "redirect_document_id": false + }, + { + "source_path": "docs/visual-basic/programming-guide/concepts/linq/how-to-read-and-write-an-encoded-document.md", + "redirect_url": "/dotnet/standard/linq/read-write-encoded-document", + "redirect_document_id": false + }, + { + "source_path": "docs/visual-basic/programming-guide/concepts/linq/how-to-use-annotation-trees-to-transform-linq-to-xml-trees-in-an-xslt-style.md", + "redirect_url": "/dotnet/standard/linq/use-annotations-transform-linq-xml-trees-xslt-style", + "redirect_document_id": false + }, + { + "source_path": "docs/visual-basic/programming-guide/concepts/linq/using-xslt-to-transform-an-xml-tree.md", + "redirect_url": "/dotnet/standard/linq/use-xslt-transform-xml-tree", + "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-perform-streaming-transform-of-large-xml-documents.md b/docs/csharp/programming-guide/concepts/linq/how-to-perform-streaming-transform-of-large-xml-documents.md deleted file mode 100644 index 33a9e94b36958..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/how-to-perform-streaming-transform-of-large-xml-documents.md +++ /dev/null @@ -1,318 +0,0 @@ ---- -title: "How to perform streaming transform of large XML documents (C#)" -ms.date: 07/20/2015 -ms.assetid: 5f16d1f8-5370-4b55-b0c8-e497df163037 ---- -# How to perform streaming transform of large XML documents (C#) -Sometimes you have to transform large XML files, and write your application so that the memory footprint of the application is predictable. If you try to populate an XML tree with a very 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. - - 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 for your application. - -Even if you use the technique described in [How to stream XML fragments with access to header information (C#)](./how-to-stream-xml-fragments-with-access-to-header-information.md), if you try to assemble an XML tree that contains the transformed document, memory usage will be too great. - - There are two main approaches. One approach is to use the deferred processing characteristics of . Another approach is to create an , and use the capabilities of [!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)] to write elements to an . This topic demonstrates both approaches. - -## Example - The following example builds on the example in [How to stream XML fragments with access to header information (C#)](./how-to-stream-xml-fragments-with-access-to-header-information.md). - - This example uses the deferred execution capabilities of to stream the output. This example can transform a very large document while maintaining a small memory footprint. - - Note that the custom axis (`StreamCustomerItem`) is specifically written so that it expects a document that has `Customer`, `Name`, and `Item` elements, and that those elements will be arranged as in the following Source.xml document. A more robust implementation, however, would be prepared to parse an invalid document. - - The following is the source document, Source.xml: - -```xml - - - - A. Datum Corporation - - 0001 - - - 0002 - - - 0003 - - - 0004 - - - - Fabrikam, Inc. - - 0005 - - - 0006 - - - 0007 - - - 0008 - - - - Southridge Video - - 0009 - - - 0010 - - - -``` - -```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) -{ - XStreamingElement root = new XStreamingElement("Root", - from el in StreamCustomerItem("Source.xml") - select new XElement("Item", - new XElement("Customer", (string)el.Parent.Element("Name")), - new XElement(el.Element("Key")) - ) - ); - root.Save("Test.xml"); - Console.WriteLine(File.ReadAllText("Test.xml")); -} -``` - - This code produces the following output: - -```xml - - - - A. Datum Corporation - 0001 - - - A. Datum Corporation - 0002 - - - A. Datum Corporation - 0003 - - - A. Datum Corporation - 0004 - - - Fabrikam, Inc. - 0005 - - - Fabrikam, Inc. - 0006 - - - Fabrikam, Inc. - 0007 - - - Fabrikam, Inc. - 0008 - - - Southridge Video - 0009 - - - Southridge Video - 0010 - - -``` - -## Example -The following example also builds on the example in [How to stream XML fragments with access to header information (C#)](./how-to-stream-xml-fragments-with-access-to-header-information.md). - - This example uses the capability of [!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)] to write elements to an . This example can transform a very large document while maintaining a small memory footprint. - - Note that the custom axis (`StreamCustomerItem`) is specifically written so that it expects a document that has `Customer`, `Name`, and `Item` elements, and that those elements will be arranged as in the following Source.xml document. A more robust implementation, however, would either validate the source document with an XSD, or would be prepared to parse an invalid document. - - This example uses the same source document, Source.xml, as the previous example in this topic. It also produces exactly the same output. - - Using for streaming the output XML is preferred over writing to an . - -```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) -{ - IEnumerable srcTree = - from el in StreamCustomerItem("Source.xml") - select new XElement("Item", - new XElement("Customer", (string)el.Parent.Element("Name")), - new XElement(el.Element("Key")) - ); - XmlWriterSettings xws = new XmlWriterSettings(); - xws.OmitXmlDeclaration = true; - xws.Indent = true; - using (XmlWriter xw = XmlWriter.Create("Output.xml", xws)) { - xw.WriteStartElement("Root"); - foreach (XElement el in srcTree) - el.WriteTo(xw); - xw.WriteEndElement(); - } - - string str = File.ReadAllText("Output.xml"); - Console.WriteLine(str); -} -``` - - This code produces the following output: - -```xml - - - A. Datum Corporation - 0001 - - - A. Datum Corporation - 0002 - - - A. Datum Corporation - 0003 - - - A. Datum Corporation - 0004 - - - Fabrikam, Inc. - 0005 - - - Fabrikam, Inc. - 0006 - - - Fabrikam, Inc. - 0007 - - - Fabrikam, Inc. - 0008 - - - Southridge Video - 0009 - - - Southridge Video - 0010 - - -``` - \ No newline at end of file diff --git a/docs/csharp/programming-guide/concepts/linq/how-to-read-and-write-an-encoded-document.md b/docs/csharp/programming-guide/concepts/linq/how-to-read-and-write-an-encoded-document.md deleted file mode 100644 index 26c4635af65f4..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/how-to-read-and-write-an-encoded-document.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -title: "How to read and write an encoded document (C#)" -ms.date: 07/20/2015 -ms.assetid: 84f64e71-39a6-42c6-ad68-f052bb158a03 ---- -# How to read and write an encoded document (C#) -To create an encoded XML document, you add an to the XML tree, setting the encoding to the desired code page name. - - Any value returned by is a valid value. - - If you read an encoded document, the property will be set to the code page name. - - If you set to a valid code page name, [!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)] will serialize with the specified encoding. - -## Example - The following example creates two documents, one with utf-8 encoding, and one with utf-16 encoding. It then loads the documents and prints the encoding to the console. - -```csharp -Console.WriteLine("Creating a document with utf-8 encoding"); -XDocument encodedDoc8 = new XDocument( - new XDeclaration("1.0", "utf-8", "yes"), - new XElement("Root", "Content") -); -encodedDoc8.Save("EncodedUtf8.xml"); -Console.WriteLine("Encoding is:{0}", encodedDoc8.Declaration.Encoding); -Console.WriteLine(); - -Console.WriteLine("Creating a document with utf-16 encoding"); -XDocument encodedDoc16 = new XDocument( - new XDeclaration("1.0", "utf-16", "yes"), - new XElement("Root", "Content") -); -encodedDoc16.Save("EncodedUtf16.xml"); -Console.WriteLine("Encoding is:{0}", encodedDoc16.Declaration.Encoding); -Console.WriteLine(); - -XDocument newDoc8 = XDocument.Load("EncodedUtf8.xml"); -Console.WriteLine("Encoded document:"); -Console.WriteLine(File.ReadAllText("EncodedUtf8.xml")); -Console.WriteLine(); -Console.WriteLine("Encoding of loaded document is:{0}", newDoc8.Declaration.Encoding); -Console.WriteLine(); - -XDocument newDoc16 = XDocument.Load("EncodedUtf16.xml"); -Console.WriteLine("Encoded document:"); -Console.WriteLine(File.ReadAllText("EncodedUtf16.xml")); -Console.WriteLine(); -Console.WriteLine("Encoding of loaded document is:{0}", newDoc16.Declaration.Encoding); -``` - - This example produces the following output: - -```output -Creating a document with utf-8 encoding -Encoding is:utf-8 - -Creating a document with utf-16 encoding -Encoding is:utf-16 - -Encoded document: - -Content - -Encoding of loaded document is:utf-8 - -Encoded document: - -Content - -Encoding of loaded document is:utf-16 -``` - -## See also - -- diff --git a/docs/csharp/programming-guide/concepts/linq/how-to-use-annotations-to-transform-linq-to-xml-trees-in-an-xslt-style.md b/docs/csharp/programming-guide/concepts/linq/how-to-use-annotations-to-transform-linq-to-xml-trees-in-an-xslt-style.md deleted file mode 100644 index 56015e5e37753..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/how-to-use-annotations-to-transform-linq-to-xml-trees-in-an-xslt-style.md +++ /dev/null @@ -1,421 +0,0 @@ ---- -title: "How to use annotations to transform LINQ to XML trees in an XSLT style (C#)" -ms.date: 07/20/2015 -ms.assetid: 12a95902-a6b7-4a1e-ad52-04a518db226f ---- -# How to use annotations to transform LINQ to XML trees in an XSLT style (C#) -Annotations can be used to facilitate transforms of an XML tree. - - Some XML documents are "document centric with mixed content." With such documents, you don't necessarily know the shape of child nodes of an element. For instance, a node that contains text may look like this: - -```xml -A phrase with bold and italic text. -``` - - For any given text node, there may be any number of child `` and `` elements. This approach extends to a number of other situations, such as pages that can contain a variety of child elements, such as regular paragraphs, bulleted paragraphs, and bitmaps. Cells in a table may contain text, drop down lists, or bitmaps. One of the primary characteristics of document centric XML is that you do not know which child element any particular element will have. - - If you want to transform elements in a tree where you don't necessarily know much about the children of the elements that you want to transform, then this approach that uses annotations is an effective approach. - - The summary of the approach is: - -- First, annotate elements in the tree with a replacement element. - -- Second, iterate through the entire tree, creating a new tree where you replace each element with its annotation. This example implements the iteration and creation of the new tree in a function named `XForm`. - - In detail, the approach consists of: - -- Execute one or more LINQ to XML queries that return the set of elements that you want to transform from one shape to another. For each element in the query, add a new object as an annotation to the element. This new element will replace the annotated element in the new, transformed tree. This is simple code to write, as demonstrated by the example. - -- The new element that is added as an annotation can contain new child nodes; it can form a sub-tree with any desired shape. - -- There is a special rule: If a child node of the new element is in a different namespace, a namespace that is made up for this purpose (in this example, the namespace is `http://www.microsoft.com/LinqToXmlTransform/2007`), then that child element is not copied to the new tree. Instead, if the namespace is the above mentioned special namespace, and the local name of the element is `ApplyTransforms`, then the child nodes of the element in the source tree are iterated, and copied to the new tree (with the exception that annotated child elements are themselves transformed according to these rules). - -- This is somewhat analogous to the specification of transforms in XSL. The query that selects a set of nodes is analogous to the XPath expression for a template. The code to create the new that is saved as an annotation is analogous to the sequence constructor in XSL, and the `ApplyTransforms` element is analogous in function to the `xsl:apply-templates` element in XSL. - -- One advantage to taking this approach - as you formulate queries, you are always writing queries on the unmodified source tree. You need not worry about how modifications to the tree affect the queries that you are writing. - -## Transforming a Tree - This first example renames all `Paragraph` nodes to `para`. - -```csharp -XNamespace xf = "http://www.microsoft.com/LinqToXmlTransform/2007"; -XName at = xf + "ApplyTransforms"; - -XElement root = XElement.Parse(@" - - This is a sentence with bold and italic text. - More text. -"); - -// replace Paragraph with para -foreach (var el in root.Descendants("Paragraph")) - el.AddAnnotation( - new XElement("para", - // same idea as xsl:apply-templates - new XElement(xf + "ApplyTransforms") - ) - ); - -// The XForm method, shown later in this topic, accomplishes the transform -XElement newRoot = XForm(root); - -Console.WriteLine(newRoot); -``` - - This example produces the following output: - -```xml - - This is a sentence with bold and italic text. - More text. - -``` - -## A More Complicated Transform - The following example queries the tree and calculates the average and sum of the `Data` elements, and adds them as new elements to the tree. - -```csharp -XNamespace xf = "http://www.microsoft.com/LinqToXmlTransform/2007"; -XName at = xf + "ApplyTransforms"; - -XElement data = new XElement("Root", - new XElement("Data", 20), - new XElement("Data", 10), - new XElement("Data", 3) -); - -// while adding annotations, you can query the source tree all you want, -// as the tree is not mutated while annotating. -var avg = data.Elements("Data").Select(z => (Decimal)z).Average(); -data.AddAnnotation( - new XElement("Root", - new XElement(xf + "ApplyTransforms"), - new XElement("Average", $"{avg:F4}"), - new XElement("Sum", - data - .Elements("Data") - .Select(z => (int)z) - .Sum() - ) - ) -); - -Console.WriteLine("Before Transform"); -Console.WriteLine("----------------"); -Console.WriteLine(data); -Console.WriteLine(); -Console.WriteLine(); - -// The XForm method, shown later in this topic, accomplishes the transform -XElement newData = XForm(data); - -Console.WriteLine("After Transform"); -Console.WriteLine("----------------"); -Console.WriteLine(newData); -``` - - This example produces the following output: - -```output -Before Transform ----------------- - - 20 - 10 - 3 - - -After Transform ----------------- - - 20 - 10 - 3 - 11.0000 - 33 - -``` - -## Effecting the Transform - A small function, `XForm`, creates a new transformed tree from the original, annotated tree. - -- The pseudo code for the function is quite simple: - -```text -The function takes an XElement as an argument and returns an XElement. -If an element has an XElement annotation, then - Return a new XElement - The name of the new XElement is the annotation element's name. - All attributes are copied from the annotation to the new node. - All child nodes are copied from the annotation, with the - exception that the special node xf:ApplyTransforms is - recognized, and the source element's child nodes are - iterated. If the source child node is not an XElement, it - is copied to the new tree. If the source child is an - XElement, then it is transformed by calling this function - recursively. -If an element is not annotated - Return a new XElement - The name of the new XElement is the source element's name - All attributes are copied from the source element to the - destination's element. - All child nodes are copied from the source element. - If the source child node is not an XElement, it is copied to - the new tree. If the source child is an XElement, then it - is transformed by calling this function recursively. -``` - - Following is the implementation of this function: - -```csharp -// Build a transformed XML tree per the annotations -static XElement XForm(XElement source) -{ - XNamespace xf = "http://www.microsoft.com/LinqToXmlTransform/2007"; - XName at = xf + "ApplyTransforms"; - - if (source.Annotation() != null) - { - XElement anno = source.Annotation(); - return new XElement(anno.Name, - anno.Attributes(), - anno - .Nodes() - .Select( - (XNode n) => - { - XElement annoEl = n as XElement; - if (annoEl != null) - { - if (annoEl.Name == at) - return (object)( - source.Nodes() - .Select( - (XNode n2) => - { - XElement e2 = n2 as XElement; - if (e2 == null) - return n2; - else - return XForm(e2); - } - ) - ); - else - return n; - } - else - return n; - } - ) - ); - } - else - { - return new XElement(source.Name, - source.Attributes(), - source - .Nodes() - .Select(n => - { - XElement el = n as XElement; - if (el == null) - return n; - else - return XForm(el); - } - ) - ); - } -} -``` - -## Complete Example - The following code is a complete example that includes the `XForm` function. It includes a few of the typical uses of this type of transform: - -```csharp -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Xml; -using System.Xml.Linq; - -class Program -{ - static XNamespace xf = "http://www.microsoft.com/LinqToXmlTransform/2007"; - static XName at = xf + "ApplyTransforms"; - - // Build a transformed XML tree per the annotations - static XElement XForm(XElement source) - { - if (source.Annotation() != null) - { - XElement anno = source.Annotation(); - return new XElement(anno.Name, - anno.Attributes(), - anno - .Nodes() - .Select( - (XNode n) => - { - XElement annoEl = n as XElement; - if (annoEl != null) - { - if (annoEl.Name == at) - return (object)( - source.Nodes() - .Select( - (XNode n2) => - { - XElement e2 = n2 as XElement; - if (e2 == null) - return n2; - else - return XForm(e2); - } - ) - ); - else - return n; - } - else - return n; - } - ) - ); - } - else - { - return new XElement(source.Name, - source.Attributes(), - source - .Nodes() - .Select(n => - { - XElement el = n as XElement; - if (el == null) - return n; - else - return XForm(el); - } - ) - ); - } - } - - static void Main(string[] args) - { - XElement root = new XElement("Root", - new XComment("A comment"), - new XAttribute("Att1", 123), - new XElement("Child", 1), - new XElement("Child", 2), - new XElement("Other", - new XElement("GC", 3), - new XElement("GC", 4) - ), - XElement.Parse( - "This is an element that " + - "has some mixed content"), - new XElement("AnUnchangedElement", 42) - ); - - // each of the following serves the same semantic purpose as - // XSLT templates and sequence constructors - - // replace Child with NewChild - foreach (var el in root.Elements("Child")) - el.AddAnnotation(new XElement("NewChild", (string)el)); - - // replace first GC with GrandChild, add an attribute - foreach (var el in root.Descendants("GC").Take(1)) - el.AddAnnotation( - new XElement("GrandChild", - new XAttribute("ANewAttribute", 999), - (string)el - ) - ); - - // replace Other with NewOther, add new child elements around original content - foreach (var el in root.Elements("Other")) - el.AddAnnotation( - new XElement("NewOther", - new XElement("MyNewChild", 1), - // same idea as xsl:apply-templates - new XElement(xf + "ApplyTransforms"), - new XElement("ChildThatComesAfter") - ) - ); - - // change name of element that has mixed content - root.Descendants("SomeMixedContent").First().AddAnnotation( - new XElement("MixedContent", - new XElement(xf + "ApplyTransforms") - ) - ); - - // replace with - foreach (var el in root.Descendants("b")) - el.AddAnnotation( - new XElement("Bold", - new XElement(xf + "ApplyTransforms") - ) - ); - - // replace with - foreach (var el in root.Descendants("i")) - el.AddAnnotation( - new XElement("Italic", - new XElement(xf + "ApplyTransforms") - ) - ); - - Console.WriteLine("Before Transform"); - Console.WriteLine("----------------"); - Console.WriteLine(root); - Console.WriteLine(); - Console.WriteLine(); - XElement newRoot = XForm(root); - - Console.WriteLine("After Transform"); - Console.WriteLine("----------------"); - Console.WriteLine(newRoot); - } -} -``` - - This example produces the following output: - -```output -Before Transform ----------------- - - - 1 - 2 - - 3 - 4 - - This is an element that has some mixed content - 42 - - -After Transform ----------------- - - - 1 - 2 - - 1 - 3 - 4 - - - This is an element that has some mixed content - 42 - -``` - \ No newline at end of file diff --git a/docs/csharp/programming-guide/concepts/linq/using-xslt-to-transform-an-xml-tree.md b/docs/csharp/programming-guide/concepts/linq/using-xslt-to-transform-an-xml-tree.md deleted file mode 100644 index a0a4b74477fc8..0000000000000 --- a/docs/csharp/programming-guide/concepts/linq/using-xslt-to-transform-an-xml-tree.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -title: "Using XSLT to Transform an XML Tree (C#)" -ms.date: 07/20/2015 -ms.assetid: 373a2699-d4c5-471b-9bda-c1f0ab73b477 ---- -# Using XSLT to Transform an XML Tree (C#) -You can create an XML tree, create an from the XML tree, create a new document, and create an that will write into the new document. Then, you can invoke the XSLT transformation, passing the and to the transformation. After the transformation successfully completes, the new XML tree is populated with the results of the transform. - -## Example - -```csharp -string xslt = @" - - - - - - - - - - - - "; - -var oldDocument = new XDocument( - new XElement("Parent", - new XElement("Child1", "Child1 data"), - new XElement("Child2", "Child2 data") - ) -); - -var newDocument = new XDocument(); - -using (var stringReader = new StringReader(xslt)) -{ - using (XmlReader xsltReader = XmlReader.Create(stringReader)) - { - var transformer = new XslCompiledTransform(); - transformer.Load(xsltReader); - using (XmlReader oldDocumentReader = oldDocument.CreateReader()) - { - using (XmlWriter newDocumentWriter = newDocument.CreateWriter()) - { - transformer.Transform(oldDocumentReader, newDocumentWriter); - } - } - } -} - -string result = newDocument.ToString(); -Console.WriteLine(result); -``` - - This example produces the following output: - -```xml - - Child1 data - Child2 data - -``` - -## See also - -- -- diff --git a/docs/standard/linq/perform-streaming-transform-large-xml-documents.md b/docs/standard/linq/perform-streaming-transform-large-xml-documents.md new file mode 100644 index 0000000000000..451841998a426 --- /dev/null +++ b/docs/standard/linq/perform-streaming-transform-large-xml-documents.md @@ -0,0 +1,586 @@ +--- +title: How to perform streaming transform of large XML documents - LINQ to XML +description: Learn how to perform streaming transform of large XML documents to achieve a small memory footprint. +ms.date: 07/20/2015 +dev_langs: + - "csharp" + - "vb" +ms.assetid: 5f16d1f8-5370-4b55-b0c8-e497df163037 +--- + +# How to perform streaming transform of large XML documents (LINQ to XML) + +Sometimes you have to transform large XML files, and write your application so that the memory footprint of the application is predictable. If you try to populate an XML tree with a very 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. + +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 won't retain a small memory footprint for your application. + +Even if you use the technique described in [How to stream XML fragments with access to header information](stream-xml-fragments-access-header-information.md), if you try to assemble an XML tree that contains the transformed document, memory usage will be too great. + +There are two main approaches. One approach is to use the deferred processing characteristics of . Another approach is to create an , and use the capabilities of LINQ to XML to write elements to an . This article demonstrates both approaches. + +## Example: Use the deferred execution capabilities of `XStreamingElement` to stream the output + +The following example builds on the example in [How to stream XML fragments with access to header information](stream-xml-fragments-access-header-information.md). + +This example uses the deferred execution capabilities of to stream the output. This example can transform a very large document while maintaining a small memory footprint. + +Note that the custom axis (`StreamCustomerItem`) is specifically written so that it expects a document that has `Customer`, `Name`, and `Item` elements, and that those elements will be arranged as in the following Source.xml document. A more robust implementation, however, would be prepared to parse an invalid document. + +The following is the source document, Source.xml: + +```xml + + + + A. Datum Corporation + + 0001 + + + 0002 + + + 0003 + + + 0004 + + + + Fabrikam, Inc. + + 0005 + + + 0006 + + + 0007 + + + 0008 + + + + Southridge Video + + 0009 + + + 0010 + + + +``` + +```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) +{ + XStreamingElement root = new XStreamingElement("Root", + from el in StreamCustomerItem("Source.xml") + select new XElement("Item", + new XElement("Customer", (string)el.Parent.Element("Name")), + new XElement(el.Element("Key")) + ) + ); + root.Save("Test.xml"); + Console.WriteLine(File.ReadAllText("Test.xml")); +} +``` + +```vb +Module Module1 + Sub Main() + Dim root = New XStreamingElement("Root", + From el In New StreamCustomerItem("Source.xml") + Select + <%= el.Parent..Value %> + <%= el. %> + + ) + root.Save("Test.xml") + Console.WriteLine(My.Computer.FileSystem.ReadAllText("Test.xml")) + 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 example produces the following output: + +```xml + + + + A. Datum Corporation + 0001 + + + A. Datum Corporation + 0002 + + + A. Datum Corporation + 0003 + + + A. Datum Corporation + 0004 + + + Fabrikam, Inc. + 0005 + + + Fabrikam, Inc. + 0006 + + + Fabrikam, Inc. + 0007 + + + Fabrikam, Inc. + 0008 + + + Southridge Video + 0009 + + + Southridge Video + 0010 + + +``` + +## Example: Use LINQ to XML to write elements to an XmlWriter + +The following example also builds on the example in [How to stream XML fragments with access to header information](stream-xml-fragments-access-header-information.md). + +This example uses the capability of LINQ to XML to write elements to an . This example can transform a very large document while maintaining a small memory footprint. + +Note that the custom axis (`StreamCustomerItem`) is specifically written so that it expects a document that has `Customer`, `Name`, and `Item` elements, and that those elements will be arranged as in the following Source.xml document. A more robust implementation, however, would either validate the source document with an XSD, or would be prepared to parse an invalid document. + +This example uses the same source document, Source.xml, as the previous example. It also produces exactly the same output. + +Using for streaming the output XML is preferred to writing to an . + +```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) +{ + IEnumerable srcTree = + from el in StreamCustomerItem("Source.xml") + select new XElement("Item", + new XElement("Customer", (string)el.Parent.Element("Name")), + new XElement(el.Element("Key")) + ); + XmlWriterSettings xws = new XmlWriterSettings(); + xws.OmitXmlDeclaration = true; + xws.Indent = true; + using (XmlWriter xw = XmlWriter.Create("Output.xml", xws)) { + xw.WriteStartElement("Root"); + foreach (XElement el in srcTree) + el.WriteTo(xw); + xw.WriteEndElement(); + } + + string str = File.ReadAllText("Output.xml"); + Console.WriteLine(str); +} +``` + +```vb +Module Module1 + Sub Main() + Dim srcTree = + From el In New StreamCustomerItem("Source.xml") + Select + <%= el.Parent..Value %> + <%= el. %> + + + Dim xws = New Xml.XmlWriterSettings() + xws.OmitXmlDeclaration = True + xws.Indent = True + Using xw = Xml.XmlWriter.Create("Output.xml", xws) + xw.WriteStartElement("Root") + For Each el In srcTree + el.WriteTo(xw) + Next + xw.WriteEndElement() + End Using + + Dim s = My.Computer.FileSystem.ReadAllText("Output.xml") + Console.WriteLine(s) + 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 example produces the following output: + +```xml + + + A. Datum Corporation + 0001 + + + A. Datum Corporation + 0002 + + + A. Datum Corporation + 0003 + + + A. Datum Corporation + 0004 + + + Fabrikam, Inc. + 0005 + + + Fabrikam, Inc. + 0006 + + + Fabrikam, Inc. + 0007 + + + Fabrikam, Inc. + 0008 + + + Southridge Video + 0009 + + + Southridge Video + 0010 + + +``` diff --git a/docs/standard/linq/program-nodes.md b/docs/standard/linq/program-nodes.md index bfb0fe4a031d3..39f8ce1f7af32 100644 --- a/docs/standard/linq/program-nodes.md +++ b/docs/standard/linq/program-nodes.md @@ -10,7 +10,7 @@ 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. +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 article provides information about programming at the node level. ## Example: The `Parent` property values of the child nodes of XDocument are set to `null` diff --git a/docs/visual-basic/programming-guide/concepts/linq/how-to-read-and-write-an-encoded-document.md b/docs/standard/linq/read-write-encoded-document.md similarity index 59% rename from docs/visual-basic/programming-guide/concepts/linq/how-to-read-and-write-an-encoded-document.md rename to docs/standard/linq/read-write-encoded-document.md index 60d5bdb7453e1..1af657d2a32e5 100644 --- a/docs/visual-basic/programming-guide/concepts/linq/how-to-read-and-write-an-encoded-document.md +++ b/docs/standard/linq/read-write-encoded-document.md @@ -1,9 +1,14 @@ --- -title: "How to: Read and Write an Encoded Document" +title: How to read and write an encoded document - LINQ to XML +description: ms.date: 07/20/2015 -ms.assetid: 159d868f-5ac8-40f2-95ca-07dd925f35c6 +dev_langs: + - "csharp" + - "vb" +ms.assetid: 84f64e71-39a6-42c6-ad68-f052bb158a03 --- -# How to: Read and Write an Encoded Document (Visual Basic) + +# How to read and write an encoded document (LINQ to XML) To create an encoded XML document, you add an to the XML tree, setting the encoding to the desired code page name. @@ -11,12 +16,45 @@ Any value returned by is a valid value. If you read an encoded document, the property will be set to the code page name. -If you set to a valid code page name, [!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)] will serialize with the specified encoding. +If you set to a valid code page name, LINQ to XML will serialize with the specified encoding. -## Example +## Example: Create two documents that have different encoding and identify the encoding. The following example creates two documents, one with utf-8 encoding, and one with utf-16 encoding. It then loads the documents and prints the encoding to the console. +```csharp +Console.WriteLine("Creating a document with utf-8 encoding"); +XDocument encodedDoc8 = new XDocument( + new XDeclaration("1.0", "utf-8", "yes"), + new XElement("Root", "Content") +); +encodedDoc8.Save("EncodedUtf8.xml"); +Console.WriteLine("Encoding is:{0}", encodedDoc8.Declaration.Encoding); +Console.WriteLine(); + +Console.WriteLine("Creating a document with utf-16 encoding"); +XDocument encodedDoc16 = new XDocument( + new XDeclaration("1.0", "utf-16", "yes"), + new XElement("Root", "Content") +); +encodedDoc16.Save("EncodedUtf16.xml"); +Console.WriteLine("Encoding is:{0}", encodedDoc16.Declaration.Encoding); +Console.WriteLine(); + +XDocument newDoc8 = XDocument.Load("EncodedUtf8.xml"); +Console.WriteLine("Encoded document:"); +Console.WriteLine(File.ReadAllText("EncodedUtf8.xml")); +Console.WriteLine(); +Console.WriteLine("Encoding of loaded document is:{0}", newDoc8.Declaration.Encoding); +Console.WriteLine(); + +XDocument newDoc16 = XDocument.Load("EncodedUtf16.xml"); +Console.WriteLine("Encoded document:"); +Console.WriteLine(File.ReadAllText("EncodedUtf16.xml")); +Console.WriteLine(); +Console.WriteLine("Encoding of loaded document is:{0}", newDoc16.Declaration.Encoding); +``` + ```vb Console.WriteLine("Creating a document with utf-8 encoding") Dim encodedDoc8 As XDocument = _ @@ -52,7 +90,7 @@ Console.WriteLine("Encoding of loaded document is:{0}", newDoc16.Declaration.Enc This example produces the following output: -```console +```output Creating a document with utf-8 encoding Encoding is:utf-8 @@ -75,4 +113,3 @@ Encoding of loaded document is:utf-16 ## See also - -- [Advanced LINQ to XML Programming (Visual Basic)](advanced-linq-to-xml-programming.md) diff --git a/docs/standard/linq/statically-compiled-queries.md b/docs/standard/linq/statically-compiled-queries.md index 03523017210fc..12a20990ec4a4 100644 --- a/docs/standard/linq/statically-compiled-queries.md +++ b/docs/standard/linq/statically-compiled-queries.md @@ -9,7 +9,7 @@ ms.assetid: 3bf558fe-0705-479d-86d4-00188f5fcf9c --- # Statically compiled queries (LINQ to XML) -One of the most important performance advantages of LINQ to XML, as compared to , is that queries in LINQ to XML are statically compiled, whereas XPath queries must be interpreted at run time. This feature is built into LINQ to XML, so you don't have to perform extra steps to take advantage of it, but it's helpful to understand the distinction when choosing between the two technologies. This topic explains the difference. +One of the most important performance advantages of LINQ to XML, as compared to , is that queries in LINQ to XML are statically compiled, whereas XPath queries must be interpreted at run time. This feature is built into LINQ to XML, so you don't have to perform extra steps to take advantage of it, but it's helpful to understand the distinction when choosing between the two technologies. This article explains the difference. ## Statically compiled queries vs. XPath diff --git a/docs/standard/linq/use-annotations-transform-linq-xml-trees-xslt-style.md b/docs/standard/linq/use-annotations-transform-linq-xml-trees-xslt-style.md new file mode 100644 index 0000000000000..d8015e5c7f17e --- /dev/null +++ b/docs/standard/linq/use-annotations-transform-linq-xml-trees-xslt-style.md @@ -0,0 +1,645 @@ +--- +title: How to use annotations to transform LINQ to XML trees in an XSLT style - LINQ to XML +description: +ms.date: 07/20/2015 +dev_langs: + - "csharp" + - "vb" +ms.assetid: 12a95902-a6b7-4a1e-ad52-04a518db226f +--- +# How to use annotations to transform LINQ to XML trees in an XSLT style (LINQ to XML) + +Annotations can be used to facilitate transforms of an XML tree. + +Some XML documents are "document-centric with mixed content." With such documents, you don't necessarily know the shape of child nodes of an element. For instance, a node that contains text may look like this: + +```xml +A phrase with bold and italic text. +``` + +For any given text node, there may be any number of child `` and `` elements. This approach extends to a number of other situations, such as pages that can contain a variety of child elements, which could be regular paragraphs, bulleted paragraphs, and bitmaps. Cells in a table may contain text, drop down lists, or bitmaps. One of the primary characteristics of document-centric XML is that you don't know which child element any particular element will have. + +If you want to transform elements in a tree where you don't necessarily know much about the children of the elements that you want to transform, then this approach that uses annotations is an effective one. + +The summary of the approach is: + +- First, annotate elements in the tree with a replacement element. +- Second, iterate through the entire tree, creating a new tree where you replace each element with its annotation. The examples in this article implement the iteration and creation of the new tree in a function named `XForm`. + +In detail, the approach consists of: + +- Execute one or more LINQ to XML queries that return the set of elements that you want to transform from one shape to another. For each element in the query, add a new object as an annotation to the element. This new element will replace the annotated element in the new, transformed tree. This is simple code to write, as demonstrated by the example. +- The new element that's added as an annotation can contain new child nodes; it can form a subtree with any desired shape. +- There is a special rule: If a child node of the new element is in a different namespace, a namespace that's made up for this purpose (in this example, the namespace is `http://www.microsoft.com/LinqToXmlTransform/2007`), then that child element isn't copied to the new tree. Instead, if the namespace is the above-mentioned special namespace, and the local name of the element is `ApplyTransforms`, then the child nodes of the element in the source tree are iterated, and copied to the new tree (with the exception that annotated child elements are themselves transformed according to these rules). +- This is somewhat analogous to the specification of transforms in XSL. The query that selects a set of nodes is analogous to the XPath expression for a template. The code to create the new that's saved as an annotation is analogous to the sequence constructor in XSL, and the `ApplyTransforms` element is analogous in function to the `xsl:apply-templates` element in XSL. +- One advantage to taking this approach is that, as you formulate queries, you are always writing queries on the unmodified source tree. You need not worry about how modifications to the tree affect the queries that you are writing. + +## Example: Rename all paragraph nodes + +This example renames all `Paragraph` nodes to `para`. + +```csharp +XNamespace xf = "http://www.microsoft.com/LinqToXmlTransform/2007"; +XName at = xf + "ApplyTransforms"; + +XElement root = XElement.Parse(@" + + This is a sentence with bold and italic text. + More text. +"); + +// replace Paragraph with para +foreach (var el in root.Descendants("Paragraph")) + el.AddAnnotation( + new XElement("para", + // same idea as xsl:apply-templates + new XElement(xf + "ApplyTransforms") + ) + ); + +// The XForm method, shown later in this article, accomplishes the transform +XElement newRoot = XForm(root); + +Console.WriteLine(newRoot); +``` + +```vb +Imports + +Module Module1 + Dim at As XName = GetXmlNamespace(xf) + "ApplyTransforms" + + Sub Main() + Dim root As XElement = _ + + This is a sentence with bold and italic text. + More text. + + + ' Replace Paragraph with p. + For Each el In root... + ' same idea as xsl:apply-templates + el.AddAnnotation( _ + + <<%= at %>> + ) + Next + + ' The XForm function, shown later in this article, accomplishes the transform + Dim newRoot As XElement = XForm(root) + Console.WriteLine(newRoot) + End Sub +End Module +``` + +This example produces the following output: + +```xml + + This is a sentence with bold and italic text. + More text. + +``` + +## Example: Calculate averages and sums and add them as new elements to the tree + +The following example calculates the average and sum of the `Data` elements and adds them as new elements to the tree. + +```csharp +XNamespace xf = "http://www.microsoft.com/LinqToXmlTransform/2007"; +XName at = xf + "ApplyTransforms"; + +XElement data = new XElement("Root", + new XElement("Data", 20), + new XElement("Data", 10), + new XElement("Data", 3) +); + +// while adding annotations, you can query the source tree all you want, +// as the tree isn't mutated while annotating. +var avg = data.Elements("Data").Select(z => (Decimal)z).Average(); +data.AddAnnotation( + new XElement("Root", + new XElement(xf + "ApplyTransforms"), + new XElement("Average", $"{avg:F4}"), + new XElement("Sum", + data + .Elements("Data") + .Select(z => (int)z) + .Sum() + ) + ) +); + +Console.WriteLine("Before Transform"); +Console.WriteLine("----------------"); +Console.WriteLine(data); +Console.WriteLine(); +Console.WriteLine(); + +// The XForm method, shown later in this article, accomplishes the transform +XElement newData = XForm(data); + +Console.WriteLine("After Transform"); +Console.WriteLine("----------------"); +Console.WriteLine(newData); +``` + +```vb +Imports + +Module Module1 + Dim at As XName = GetXmlNamespace(xf) + "ApplyTransforms" + + Sub Main() + Dim data As XElement = _ + + 20 + 10 + 3 + + + ' While adding annotations, you can query the source tree all you want, + ' as the tree isn't mutated while annotating. + data.AddAnnotation( _ + + <<%= at %>/> + + <%= _ + String.Format("{0:F4}", _ + data.Elements("Data") _ + .Select(Function(z) CDec(z)).Average()) _ + %> + + + <%= _ + data.Elements("Data").Select(Function(z) CInt(z)).Sum() _ + %> + + _ + ) + + Console.WriteLine("Before Transform") + Console.WriteLine("----------------") + Console.WriteLine(data) + Console.WriteLine(vbNewLine) + + ' The XForm function, shown later in this article, accomplishes the transform + Dim newData As XElement = XForm(data) + + Console.WriteLine("After Transform") + Console.WriteLine("----------------") + Console.WriteLine(newData) + End Sub +End Module +``` + +This example produces the following output: + +```output +Before Transform +---------------- + + 20 + 10 + 3 + + +After Transform +---------------- + + 20 + 10 + 3 + 11.0000 + 33 + +``` + +## Example: Create a new transformed tree from the original annotated tree + +A small function, `XForm`, creates a new transformed tree from the original, annotated tree. The following is pseudocode for this function: + +> The function takes an XElement as an argument and returns an XElement. +> +> If an element has an XElement annotation, the returned XElement has these characteristics: +> +> - The name of the new XElement is the annotation element's name. +> - All attributes are copied from the annotation to the new node. +> - All child nodes are copied from the annotation, with the exception that the special node xf:ApplyTransforms is recognized, and the source element's child nodes are iterated. If the source child node isn't an XElement, it's copied to the new tree. If the source child is an XElement, then it's transformed by calling this function recursively. +> +> Otherwise, the returned XElement has these characteristics: +> +> - The name of the new XElement is the source element's name. +> - All attributes are copied from the source element to the destination's element. +> - All child nodes are copied from the source element. +> - If the source child node isn't an XElement, it's copied to the new tree. If the source child is an XElement, then it's transformed by calling this function recursively. + +The following is code for this function: + +```csharp +// Build a transformed XML tree per the annotations +static XElement XForm(XElement source) +{ + XNamespace xf = "http://www.microsoft.com/LinqToXmlTransform/2007"; + XName at = xf + "ApplyTransforms"; + + if (source.Annotation() != null) + { + XElement anno = source.Annotation(); + return new XElement(anno.Name, + anno.Attributes(), + anno + .Nodes() + .Select( + (XNode n) => + { + XElement annoEl = n as XElement; + if (annoEl != null) + { + if (annoEl.Name == at) + return (object)( + source.Nodes() + .Select( + (XNode n2) => + { + XElement e2 = n2 as XElement; + if (e2 == null) + return n2; + else + return XForm(e2); + } + ) + ); + else + return n; + } + else + return n; + } + ) + ); + } + else + { + return new XElement(source.Name, + source.Attributes(), + source + .Nodes() + .Select(n => + { + XElement el = n as XElement; + if (el == null) + return n; + else + return XForm(el); + } + ) + ); + } +} +``` + +```vb +' Build a transformed XML tree per the annotations. +Function XForm(ByVal source As XElement) As XElement + If source.Annotation(Of XElement)() IsNot Nothing Then + Dim anno As XElement = source.Annotation(Of XElement)() + Return _ + <<%= anno.Name.ToString() %>> + <%= anno.Attributes() %> + <%= anno.Nodes().Select(Function(n As XNode) _ + GetSubNodes(n, source)) %> + + Else + Return _ + <<%= source.Name %>> + <%= source.Attributes() %> + <%= source.Nodes().Select(Function(n) GetExpandedNodes(n)) %> + + End If +End Function + +Private Function GetSubNodes(ByVal n As XNode, ByVal s As XElement) As Object + Dim annoEl As XElement = TryCast(n, XElement) + If annoEl IsNot Nothing Then + If annoEl.Name = at Then + Return s.Nodes().Select(Function(n2 As XNode) GetExpandedNodes(n2)) + End If + End If + Return n +End Function + +Private Function GetExpandedNodes(ByVal n2 As XNode) As XNode + Dim e2 As XElement = TryCast(n2, XElement) + If e2 Is Nothing Then + Return n2 + Else + Return XForm(e2) + End If +End Function +``` + +## Example: Show `XForm` in typical uses of this type of transform + +The following example includes the `XForm` function and a few of the typical uses of this type of transform: + +```csharp +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml; +using System.Xml.Linq; + +class Program +{ + static XNamespace xf = "http://www.microsoft.com/LinqToXmlTransform/2007"; + static XName at = xf + "ApplyTransforms"; + + // Build a transformed XML tree per the annotations + static XElement XForm(XElement source) + { + if (source.Annotation() != null) + { + XElement anno = source.Annotation(); + return new XElement(anno.Name, + anno.Attributes(), + anno + .Nodes() + .Select( + (XNode n) => + { + XElement annoEl = n as XElement; + if (annoEl != null) + { + if (annoEl.Name == at) + return (object)( + source.Nodes() + .Select( + (XNode n2) => + { + XElement e2 = n2 as XElement; + if (e2 == null) + return n2; + else + return XForm(e2); + } + ) + ); + else + return n; + } + else + return n; + } + ) + ); + } + else + { + return new XElement(source.Name, + source.Attributes(), + source + .Nodes() + .Select(n => + { + XElement el = n as XElement; + if (el == null) + return n; + else + return XForm(el); + } + ) + ); + } + } + + static void Main(string[] args) + { + XElement root = new XElement("Root", + new XComment("A comment"), + new XAttribute("Att1", 123), + new XElement("Child", 1), + new XElement("Child", 2), + new XElement("Other", + new XElement("GC", 3), + new XElement("GC", 4) + ), + XElement.Parse( + "This is an element that " + + "has some mixed content"), + new XElement("AnUnchangedElement", 42) + ); + + // each of the following serves the same semantic purpose as + // XSLT templates and sequence constructors + + // replace Child with NewChild + foreach (var el in root.Elements("Child")) + el.AddAnnotation(new XElement("NewChild", (string)el)); + + // replace first GC with GrandChild, add an attribute + foreach (var el in root.Descendants("GC").Take(1)) + el.AddAnnotation( + new XElement("GrandChild", + new XAttribute("ANewAttribute", 999), + (string)el + ) + ); + + // replace Other with NewOther, add new child elements around original content + foreach (var el in root.Elements("Other")) + el.AddAnnotation( + new XElement("NewOther", + new XElement("MyNewChild", 1), + // same idea as xsl:apply-templates + new XElement(xf + "ApplyTransforms"), + new XElement("ChildThatComesAfter") + ) + ); + + // change name of element that has mixed content + root.Descendants("SomeMixedContent").First().AddAnnotation( + new XElement("MixedContent", + new XElement(xf + "ApplyTransforms") + ) + ); + + // replace with + foreach (var el in root.Descendants("b")) + el.AddAnnotation( + new XElement("Bold", + new XElement(xf + "ApplyTransforms") + ) + ); + + // replace with + foreach (var el in root.Descendants("i")) + el.AddAnnotation( + new XElement("Italic", + new XElement(xf + "ApplyTransforms") + ) + ); + + Console.WriteLine("Before Transform"); + Console.WriteLine("----------------"); + Console.WriteLine(root); + Console.WriteLine(); + Console.WriteLine(); + XElement newRoot = XForm(root); + + Console.WriteLine("After Transform"); + Console.WriteLine("----------------"); + Console.WriteLine(newRoot); + } +} +``` + +```vb +Imports System.Collections.Generic +Imports System.Linq +Imports System.Text +Imports System.Xml +Imports System.Xml.Linq + +Imports + +Module Module1 + Dim at As XName = GetXmlNamespace(xf) + "ApplyTransforms" + + ' Build a transformed XML tree per the annotations. + Function XForm(ByVal source As XElement) As XElement + If source.Annotation(Of XElement)() IsNot Nothing Then + Dim anno As XElement = source.Annotation(Of XElement)() + Return _ + <<%= anno.Name.ToString() %>> + <%= anno.Attributes() %> + <%= anno.Nodes().Select(Function(n As XNode) _ + GetSubNodes(n, source)) %> + + Else + Return _ + <<%= source.Name %>> + <%= source.Attributes() %> + <%= source.Nodes().Select(Function(n) GetExpandedNodes(n)) %> + + End If + End Function + + Private Function GetSubNodes(ByVal n As XNode, ByVal s As XElement) As Object + Dim annoEl As XElement = TryCast(n, XElement) + If annoEl IsNot Nothing Then + If annoEl.Name = at Then + Return s.Nodes().Select(Function(n2 As XNode) GetExpandedNodes(n2)) + End If + End If + Return n + End Function + + Private Function GetExpandedNodes(ByVal n2 As XNode) As XNode + Dim e2 As XElement = TryCast(n2, XElement) + If e2 Is Nothing Then + Return n2 + Else + Return XForm(e2) + End If + End Function + + Sub Main() + Dim root As XElement = _ + + + 1 + 2 + + 3 + 4 + + This is an element that has some mixed content + 42 + + + ' Each of the following serves the same semantic purpose as + ' XSLT templates and sequence constructors. + + ' Replace Child with NewChild. + For Each el In root. + el.AddAnnotation(<%= CStr(el) %>) + Next + + ' Replace first GC with GrandChild, add an attribute. + For Each el In root....Take(1) + el.AddAnnotation(<%= CStr(el) %>) + Next + + ' Replace Other with NewOther, add new child elements around original content. + For Each el In root. + el.AddAnnotation( _ + + 1 + <<%= at %>> + + ) + Next + + ' Change name of element that has mixed content. + root...(0).AddAnnotation( _ + <<%= at %>>) + + ' Replace with . + For Each el In root... + el.AddAnnotation(<<%= at %>>) + Next + + ' Replace with . + For Each el In root... + el.AddAnnotation(<<%= at %>>) + Next + + Console.WriteLine("Before Transform") + Console.WriteLine("----------------") + Console.WriteLine(root) + Console.WriteLine(vbNewLine) + Dim newRoot As XElement = XForm(root) + + Console.WriteLine("After Transform") + Console.WriteLine("----------------") + Console.WriteLine(newRoot) + End Sub +End Module +``` + +This example produces the following output: + +```output +Before Transform +---------------- + + + 1 + 2 + + 3 + 4 + + This is an element that has some mixed content + 42 + + +After Transform +---------------- + + + 1 + 2 + + 1 + 3 + 4 + + + This is an element that has some mixed content + 42 + +``` diff --git a/docs/standard/linq/use-xslt-transform-xml-tree.md b/docs/standard/linq/use-xslt-transform-xml-tree.md new file mode 100644 index 0000000000000..f3eafbfdba4f1 --- /dev/null +++ b/docs/standard/linq/use-xslt-transform-xml-tree.md @@ -0,0 +1,120 @@ +--- +title: Use XSLT to transform an XML tree - LINQ to XML +description: Learn to use XSLT to transform an XML tree, using XmlReader to read and XmlWriter to write. +ms.date: 07/20/2015 +dev_langs: + - "csharp" + - "vb" +ms.assetid: 373a2699-d4c5-471b-9bda-c1f0ab73b477 +--- +# Use XSLT to transform an XML tree (LINQ to XML) + +You can use XSLT to transform an XML tree, using to read and to write. + +## Example: Use XSLT to transform an XML tree, using `XMLReader` to read and `XMLWriter` to write + +This example creates an XML tree and uses XSLT to transform it. It makes use of an to read the original tree, and an to write the transformed version. + +It starts by creating: + +- An XML tree. +- An of the XML tree. +- A new document to hold the XSLT output. +- An to write the transformed tree to the new document. + +It then invokes an XSLT transformation that uses the to read the original XML tree, and the to write the transformed tree to the new document. + +```csharp +string xslt = @" + + + + + + + + + + + + "; + +var oldDocument = new XDocument( + new XElement("Parent", + new XElement("Child1", "Child1 data"), + new XElement("Child2", "Child2 data") + ) +); + +var newDocument = new XDocument(); + +using (var stringReader = new StringReader(xslt)) +{ + using (XmlReader xsltReader = XmlReader.Create(stringReader)) + { + var transformer = new XslCompiledTransform(); + transformer.Load(xsltReader); + using (XmlReader oldDocumentReader = oldDocument.CreateReader()) + { + using (XmlWriter newDocumentWriter = newDocument.CreateWriter()) + { + transformer.Transform(oldDocumentReader, newDocumentWriter); + } + } + } +} + +string result = newDocument.ToString(); +Console.WriteLine(result); +``` + +```vb +Dim xslMarkup As XDocument = _ + + + + + + + + + + + + + + +Dim xmlTree As XElement = _ + + Child1 data + Child2 data + + +Dim newTree As XDocument = New XDocument() + +Using writer As XmlWriter = newTree.CreateWriter() + ' Load the style sheet. + Dim xslt As XslCompiledTransform = _ + New XslCompiledTransform() + xslt.Load(xslMarkup.CreateReader()) + + ' Execute the transform and output the results to a writer. + xslt.Transform(xmlTree.CreateReader(), writer) +End Using + +Console.WriteLine(newTree) +``` + +This example produces the following output: + +```xml + + Child1 data + Child2 data + +``` + +## See also + +- +- diff --git a/docs/visual-basic/programming-guide/concepts/linq/how-to-perform-streaming-transform-of-large-xml-documents.md b/docs/visual-basic/programming-guide/concepts/linq/how-to-perform-streaming-transform-of-large-xml-documents.md deleted file mode 100644 index 77220283e8c77..0000000000000 --- a/docs/visual-basic/programming-guide/concepts/linq/how-to-perform-streaming-transform-of-large-xml-documents.md +++ /dev/null @@ -1,438 +0,0 @@ ---- -title: "How to: Perform Streaming Transform of Large XML Documents" -ms.date: 07/20/2015 -ms.assetid: 3d954cc9-4b3c-4b47-8132-ff7541cff53b ---- -# How to: Perform Streaming Transform of Large XML Documents (Visual Basic) -Sometimes you have to transform large XML files, and write your application so that the memory footprint of the application is predictable. If you try to populate an XML tree with a very 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. - - 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 for your application. - - Even if you use the technique described in [How to: Stream XML Fragments with Access to Header Information (Visual Basic)](how-to-stream-xml-fragments-with-access-to-header-information.md), if you try to assemble an XML tree that contains the transformed document, memory usage will be too great. - - There are two main approaches. One approach is to use the deferred processing characteristics of . Another approach is to create an , and use the capabilities of [!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)] to write elements to an . This topic demonstrates both approaches. - -## Example - The following example builds on the example in [How to: Stream XML Fragments with Access to Header Information (Visual Basic)](how-to-stream-xml-fragments-with-access-to-header-information.md). - - This example uses the deferred execution capabilities of to stream the output. This example can transform a very large document while maintaining a small memory footprint. - - Note that the custom axis (`StreamCustomerItem`) is specifically written so that it expects a document that has `Customer`, `Name`, and `Item` elements, and that those elements will be arranged as in the following Source.xml document. A more robust implementation, however, would be prepared to parse an invalid document. - - The following is the source document, Source.xml: - -```xml - - - - A. Datum Corporation - - 0001 - - - 0002 - - - 0003 - - - 0004 - - - - Fabrikam, Inc. - - 0005 - - - 0006 - - - 0007 - - - 0008 - - - - Southridge Video - - 0009 - - - 0010 - - - -``` - -```vb -Module Module1 - Sub Main() - Dim root = New XStreamingElement("Root", - From el In New StreamCustomerItem("Source.xml") - Select - <%= el.Parent..Value %> - <%= el. %> - - ) - root.Save("Test.xml") - Console.WriteLine(My.Computer.FileSystem.ReadAllText("Test.xml")) - 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 - 0001 - - - A. Datum Corporation - 0002 - - - A. Datum Corporation - 0003 - - - A. Datum Corporation - 0004 - - - Fabrikam, Inc. - 0005 - - - Fabrikam, Inc. - 0006 - - - Fabrikam, Inc. - 0007 - - - Fabrikam, Inc. - 0008 - - - Southridge Video - 0009 - - - Southridge Video - 0010 - - -``` - -## Example - The following example also builds on the example in [How to: Stream XML Fragments with Access to Header Information (Visual Basic)](how-to-stream-xml-fragments-with-access-to-header-information.md). - - This example uses the capability of [!INCLUDE[sqltecxlinq](~/includes/sqltecxlinq-md.md)] to write elements to an . This example can transform a very large document while maintaining a small memory footprint. - - Note that the custom axis (`StreamCustomerItem`) is specifically written so that it expects a document that has `Customer`, `Name`, and `Item` elements, and that those elements will be arranged as in the following Source.xml document. A more robust implementation, however, would either validate the source document with an XSD, or would be prepared to parse an invalid document. - - This example uses the same source document, Source.xml, as the previous example in this topic. It also produces exactly the same output. - - Using for streaming the output XML is preferred over writing to an . - -```vb -Module Module1 - Sub Main() - Dim srcTree = - From el In New StreamCustomerItem("Source.xml") - Select - <%= el.Parent..Value %> - <%= el. %> - - - Dim xws = New Xml.XmlWriterSettings() - xws.OmitXmlDeclaration = True - xws.Indent = True - Using xw = Xml.XmlWriter.Create("Output.xml", xws) - xw.WriteStartElement("Root") - For Each el In srcTree - el.WriteTo(xw) - Next - xw.WriteEndElement() - End Using - - Dim s = My.Computer.FileSystem.ReadAllText("Output.xml") - Console.WriteLine(s) - 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 - 0001 - - - A. Datum Corporation - 0002 - - - A. Datum Corporation - 0003 - - - A. Datum Corporation - 0004 - - - Fabrikam, Inc. - 0005 - - - Fabrikam, Inc. - 0006 - - - Fabrikam, Inc. - 0007 - - - Fabrikam, Inc. - 0008 - - - Southridge Video - 0009 - - - Southridge Video - 0010 - - -``` - -## 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/how-to-use-annotation-trees-to-transform-linq-to-xml-trees-in-an-xslt-style.md b/docs/visual-basic/programming-guide/concepts/linq/how-to-use-annotation-trees-to-transform-linq-to-xml-trees-in-an-xslt-style.md deleted file mode 100644 index 7c9ee9733a92a..0000000000000 --- a/docs/visual-basic/programming-guide/concepts/linq/how-to-use-annotation-trees-to-transform-linq-to-xml-trees-in-an-xslt-style.md +++ /dev/null @@ -1,373 +0,0 @@ ---- -title: "How to: Use Annotations to Transform LINQ to XML Trees in an XSLT Style" -ms.date: 07/20/2015 -ms.assetid: 08e91fa2-dac2-4463-9ef1-87b1ac3fa890 ---- -# How to: Use Annotations to Transform LINQ to XML Trees in an XSLT Style (Visual Basic) - -Annotations can be used to facilitate transforms of an XML tree. - -Some XML documents are "document centric with mixed content." With such documents, you don't necessarily know the shape of child nodes of an element. For instance, a node that contains text may look like this: - -```xml -A phrase with bold and italic text. -``` - -For any given text node, there may be any number of child `` and `` elements. This approach extends to a number of other situations: such as, pages that can contain a variety of child elements, such as regular paragraphs, bulleted paragraphs, and bitmaps. Cells in a table may contain text, drop down lists, or bitmaps. One of the primary characteristics of document centric XML is that you do not know which child element any particular element will have. - -If you want to transform elements in a tree where you don't necessarily know much about the children of the elements that you want to transform, then this approach that uses annotations is an effective approach. - -The summary of the approach is: - -- First, annotate elements in the tree with a replacement element. - -- Second, iterate through the entire tree, creating a new tree where you replace each element with its annotation. This example implements the iteration and creation of the new tree in a function named `XForm`. - -In detail, the approach consists of: - -- Execute one or more LINQ to XML queries that return the set of elements that you want to transform from one shape to another. For each element in the query, add a new object as an annotation to the element. This new element will replace the annotated element in the new, transformed tree. This is simple code to write, as demonstrated by the example. - -- The new element that is added as an annotation can contain new child nodes; it can form a sub-tree with any desired shape. - -- There is a special rule: If a child node of the new element is in a different namespace, a namespace that is made up for this purpose (in this example, the namespace is `http://www.microsoft.com/LinqToXmlTransform/2007`), then that child element is not copied to the new tree. Instead, if the namespace is the above mentioned special namespace, and the local name of the element is `ApplyTransforms`, then the child nodes of the element in the source tree are iterated, and copied to the new tree (with the exception that annotated child elements are themselves transformed according to these rules). - -- This is somewhat analogous to the specification of transforms in XSL. The query that selects a set of nodes is analogous to the XPath expression for a template. The code to create the new that is saved as an annotation is analogous to the sequence constructor in XSL, and the `ApplyTransforms` element is analogous in function to the `xsl:apply-templates` element in XSL. - -- One advantage to taking this approach - as you formulate queries, you are always writing queries on the unmodified source tree. You need not worry about how modifications to the tree affect the queries that you are writing. - -## Transforming a Tree - -This first example renames all `Paragraph` nodes to `para`: - -```vb -Imports - -Module Module1 - Dim at As XName = GetXmlNamespace(xf) + "ApplyTransforms" - - Sub Main() - Dim root As XElement = _ - - This is a sentence with bold and italic text. - More text. - - - ' Replace Paragraph with p. - For Each el In root... - ' same idea as xsl:apply-templates - el.AddAnnotation( _ - - <<%= at %>> - ) - Next - - ' The XForm function, shown later in this topic, accomplishes the transform - Dim newRoot As XElement = XForm(root) - Console.WriteLine(newRoot) - End Sub -End Module -``` - - This example produces the following output: - -```xml - - This is a sentence with bold and italic text. - More text. - -``` - -## A more complicated transform - -The following example queries the tree and calculates the average and sum of the `Data` elements, and adds them as new elements to the tree. - -```vb -Imports - -Module Module1 - Dim at As XName = GetXmlNamespace(xf) + "ApplyTransforms" - - Sub Main() - Dim data As XElement = _ - - 20 - 10 - 3 - - - ' While adding annotations, you can query the source tree all you want, - ' as the tree is not mutated while annotating. - data.AddAnnotation( _ - - <<%= at %>/> - - <%= _ - String.Format("{0:F4}", _ - data.Elements("Data") _ - .Select(Function(z) CDec(z)).Average()) _ - %> - - - <%= _ - data.Elements("Data").Select(Function(z) CInt(z)).Sum() _ - %> - - _ - ) - - Console.WriteLine("Before Transform") - Console.WriteLine("----------------") - Console.WriteLine(data) - Console.WriteLine(vbNewLine) - - ' The XForm function, shown later in this topic, accomplishes the transform - Dim newData As XElement = XForm(data) - - Console.WriteLine("After Transform") - Console.WriteLine("----------------") - Console.WriteLine(newData) - End Sub -End Module -``` - -This example produces the following output: - -```console -Before Transform ----------------- - - 20 - 10 - 3 - - -After Transform ----------------- - - 20 - 10 - 3 - 11.0000 - 33 - -``` - -## Effecting the transform - -A small function, `XForm`, creates a new transformed tree from the original, annotated tree. - -The pseudo code for the function is quite simple: - -> The function takes an XElement as an argument and returns an XElement. -> -> If an element has an XElement annotation, then return a new XElement: -> -> - The name of the new XElement is the annotation element's name. -> - All attributes are copied from the annotation to the new node. -> - All child nodes are copied from the annotation, with the exception that the special node xf:ApplyTransforms is recognized, and the source element's child nodes are iterated. If the source child node is not an XElement, it is copied to the new tree. If the source child is an XElement, then it is transformed by calling this function recursively. -> -> If an element is not annotated: -> -> - Return a new XElement -> - The name of the new XElement is the source element's name. -> - All attributes are copied from the source element to the destination's element. -> - All child nodes are copied from the source element. -> - If the source child node is not an XElement, it is copied to the new tree. If the source child is an XElement, then it is transformed by calling this function recursively. - -The following code is the implementation of this function: - -```vb -' Build a transformed XML tree per the annotations. -Function XForm(ByVal source As XElement) As XElement - If source.Annotation(Of XElement)() IsNot Nothing Then - Dim anno As XElement = source.Annotation(Of XElement)() - Return _ - <<%= anno.Name.ToString() %>> - <%= anno.Attributes() %> - <%= anno.Nodes().Select(Function(n As XNode) _ - GetSubNodes(n, source)) %> - - Else - Return _ - <<%= source.Name %>> - <%= source.Attributes() %> - <%= source.Nodes().Select(Function(n) GetExpandedNodes(n)) %> - - End If -End Function - -Private Function GetSubNodes(ByVal n As XNode, ByVal s As XElement) As Object - Dim annoEl As XElement = TryCast(n, XElement) - If annoEl IsNot Nothing Then - If annoEl.Name = at Then - Return s.Nodes().Select(Function(n2 As XNode) GetExpandedNodes(n2)) - End If - End If - Return n -End Function - -Private Function GetExpandedNodes(ByVal n2 As XNode) As XNode - Dim e2 As XElement = TryCast(n2, XElement) - If e2 Is Nothing Then - Return n2 - Else - Return XForm(e2) - End If -End Function -``` - -## Complete example - -The following code is a complete example that includes the `XForm` function. It includes a few of the typical uses of this type of transform: - -```vb -Imports System.Collections.Generic -Imports System.Linq -Imports System.Text -Imports System.Xml -Imports System.Xml.Linq - -Imports - -Module Module1 - Dim at As XName = GetXmlNamespace(xf) + "ApplyTransforms" - - ' Build a transformed XML tree per the annotations. - Function XForm(ByVal source As XElement) As XElement - If source.Annotation(Of XElement)() IsNot Nothing Then - Dim anno As XElement = source.Annotation(Of XElement)() - Return _ - <<%= anno.Name.ToString() %>> - <%= anno.Attributes() %> - <%= anno.Nodes().Select(Function(n As XNode) _ - GetSubNodes(n, source)) %> - - Else - Return _ - <<%= source.Name %>> - <%= source.Attributes() %> - <%= source.Nodes().Select(Function(n) GetExpandedNodes(n)) %> - - End If - End Function - - Private Function GetSubNodes(ByVal n As XNode, ByVal s As XElement) As Object - Dim annoEl As XElement = TryCast(n, XElement) - If annoEl IsNot Nothing Then - If annoEl.Name = at Then - Return s.Nodes().Select(Function(n2 As XNode) GetExpandedNodes(n2)) - End If - End If - Return n - End Function - - Private Function GetExpandedNodes(ByVal n2 As XNode) As XNode - Dim e2 As XElement = TryCast(n2, XElement) - If e2 Is Nothing Then - Return n2 - Else - Return XForm(e2) - End If - End Function - - Sub Main() - Dim root As XElement = _ - - - 1 - 2 - - 3 - 4 - - This is an element that has some mixed content - 42 - - - ' Each of the following serves the same semantic purpose as - ' XSLT templates and sequence constructors. - - ' Replace Child with NewChild. - For Each el In root. - el.AddAnnotation(<%= CStr(el) %>) - Next - - ' Replace first GC with GrandChild, add an attribute. - For Each el In root....Take(1) - el.AddAnnotation(<%= CStr(el) %>) - Next - - ' Replace Other with NewOther, add new child elements around original content. - For Each el In root. - el.AddAnnotation( _ - - 1 - <<%= at %>> - - ) - Next - - ' Change name of element that has mixed content. - root...(0).AddAnnotation( _ - <<%= at %>>) - - ' Replace with . - For Each el In root... - el.AddAnnotation(<<%= at %>>) - Next - - ' Replace with . - For Each el In root... - el.AddAnnotation(<<%= at %>>) - Next - - Console.WriteLine("Before Transform") - Console.WriteLine("----------------") - Console.WriteLine(root) - Console.WriteLine(vbNewLine) - Dim newRoot As XElement = XForm(root) - - Console.WriteLine("After Transform") - Console.WriteLine("----------------") - Console.WriteLine(newRoot) - End Sub -End Module -``` - -This example produces the following output: - -```console -Before Transform ----------------- - - - 1 - 2 - - 3 - 4 - - This is an element that has some mixed content - 42 - - -After Transform ----------------- - - - 1 - 2 - - 1 - 3 - 4 - - - This is an element that has some mixed content - 42 - -``` - -## 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/using-xslt-to-transform-an-xml-tree.md b/docs/visual-basic/programming-guide/concepts/linq/using-xslt-to-transform-an-xml-tree.md deleted file mode 100644 index d8cc7b1e7ac51..0000000000000 --- a/docs/visual-basic/programming-guide/concepts/linq/using-xslt-to-transform-an-xml-tree.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -title: "Using XSLT to Transform an XML Tree" -ms.date: 07/20/2015 -ms.assetid: 3390ca68-c270-4e1d-b64b-6a063a77017c ---- -# Using XSLT to Transform an XML Tree (Visual Basic) - -You can create an XML tree, create an from the XML tree, create a new document, and create an that will write into the new document. Then, you can invoke the XSLT transformation, passing the and to the transformation. After the transformation successfully completes, the new XML tree is populated with the results of the transform. - -## Example - -```vb -Dim xslMarkup As XDocument = _ - - - - - - - - - - - - - - -Dim xmlTree As XElement = _ - - Child1 data - Child2 data - - -Dim newTree As XDocument = New XDocument() - -Using writer As XmlWriter = newTree.CreateWriter() - ' Load the style sheet. - Dim xslt As XslCompiledTransform = _ - New XslCompiledTransform() - xslt.Load(xslMarkup.CreateReader()) - - ' Execute the transform and output the results to a writer. - xslt.Transform(xmlTree.CreateReader(), writer) -End Using - -Console.WriteLine(newTree) -``` - -This example produces the following output: - -```xml - - Child1 data - Child2 data - -``` - -## See also - -- -- -- [Advanced LINQ to XML Programming (Visual Basic)](advanced-linq-to-xml-programming.md)