## Chapter 15. XML

Introduction

Recipe 15.1. Reading and Accessing XML Data in Document Order

Recipe 15.2. Reading XML on the Web

Recipe 15.3. Querying the Contents of an XML Document

Recipe 15.4. Validating XML

Recipe 15.5. Creating an XML Document Programmatically

Recipe 15.6. Detecting Changes to an XML Document

Recipe 15.7. Handling Invalid Characters in an XML String

Recipe 15.8. Transforming XML

Recipe 15.9. Tearing Apart an XML Document

Recipe 15.10. Putting Together an XML Document

Recipe 15.11. Validating Modified XML Documents Without Reloading

Recipe 15.12. Extending XSLT Transformations

Recipe 15.13. Getting Your Schema in Bulk from Existing XML Files

Recipe 15.14. Passing Parameters to XSLT Transformations

### Introduction

Extensible Markup Language (XML) is a simple, portable, and flexible way to represent data in a structured format. XML is used in a myriad of ways, from acting as the foundation of web-based messaging protocols such as SOAP, to being one of the more popular ways to store configuration data (such as the web.config, machine.config, or security.config files in the .NET Framework). Microsoft recognized the usefulness of XML to developers and has done a nice job of giving you choices concerning the trade-offs involved. Sometimes you want to simply run though an XML document looking for a value in a read-only cursorlike fashion, and other times you need to be able to randomly access various pieces of the document. Microsoft provides classes like `XmlReader` and `XmlWriter` for lighter access and `XmlDocument` for full Document Object Model (DOM) processing support. In the 1.x versions of the Framework, the `XmlTextReader`, `XmlTextWriter`, and `XmlValidatingReader` classes were provided as concrete instances to be used. Microsoft is now recommending using the factory methods on the `XmlReader` and `XmlWriter` classes, in conjunction with the `XmlReaderSettings` and `XmlWriterSettings` classes to work with XML.

It is likely that you will be dealing with XML in .NET to one degree or another. So this chapter explores some of the uses for XML and XML-based technologies such as XPath and XSLT. It also explores topics such as XML validation and transformation of XML to HTML.

### Recipe 15.1. Reading and Accessing XML Data in Document Order

#### Problem

You need to read in all the elements of an XML document and obtain information about each element, such as its name and attributes.

#### Solution

Create an `XmlReader` and use its `Read` method to process the document as shown in Example 15-1.

##### Example 15-1. Reading an XML document

    using System;
    using System.Xml;

    // …

    public static void Indent(int level)
    {
        for (int i = 0; i < level; i++)
          Console.Write(" ");
    }

    public static void AccessXML( )
    {
        string xmlFragment = "<?xml version='1.0'?>" +
            "<!-- My sample XML -->" +
            "<?pi myProcessingInstruction?>" +
            "<Root>" +
            "<Node1 nodeId='1'>First Node</Node1>" +
            "<Node2 nodeId='2'>Second Node</Node2>" +
            "<Node3 nodeId='3'>Third Node</Node3>" +
            "</Root>";

        byte[] bytes = Encoding.UTF8.GetBytes(xmlFragment);
        using (MemoryStream memStream = new MemoryStream(bytes))
        {

            XmlReaderSettings settings = new XmlReaderSettings();
            // Check for any illegal characters in the XML.
            settings.CheckCharacters = true;

            using (XmlReader reader = XmlReader.Create(memStream, settings))
            {
                int level = 0;
                while (reader.Read())
                {
                    switch (reader.NodeType)
                    {
                        case XmlNodeType.CDATA:
                            Indent(level);
                            Console.WriteLine("CDATA: {0}", reader.Value);
                            break;
                        case XmlNodeType.Comment:
                            Indent(level);
                            Console.WriteLine("COMMENT: {0}", reader.Value);
                            break;
                        case XmlNodeType.DocumentType:
                            Indent(level);
                            Console.WriteLine("DOCTYPE: {0}={1}",
                                reader.Name, reader.Value);
                            break;
                        case XmlNodeType.Element:
                            Indent(level);
                            Console.WriteLine("ELEMENT: {0}", reader.Name);
                            level++;
                            while (reader.MoveToNextAttribute())
                            {
                                Indent(level);
                                Console.WriteLine("ATTRIBUTE: {0}='{1}'",
                                    reader.Name, reader.Value);
                            }
                            break;
                        case XmlNodeType.EndElement:
                            level--;
                            break;
                        case XmlNodeType.EntityReference:
                            Indent(level);
                            Console.WriteLine("ENTITY: {0}", reader.Name);
                            break;
                        case XmlNodeType.ProcessingInstruction:
                            Indent(level);
                            Console.WriteLine("INSTRUCTION: {0}={1}",
                                reader.Name, reader.Value);
                            break;
                        case XmlNodeType.Text:
                            Indent(level);
                            Console.WriteLine("TEXT: {0}", reader.Value);
                            break;
                        case XmlNodeType.XmlDeclaration:
                            Indent(level);
                            Console.WriteLine("DECLARATION: {0}={1}",
                                reader.Name, reader.Value);
                            break;
                    }
                }
            }
        }
    }

This code dumps the XML document in a hierarchical format:

    	DECLARATION: xml=version='1.0'
    	COMMENT: My sample XML
    	INSTRUCTION: pi=myProcessingInstruction
    	ELEMENT: Root
    	 ELEMENT: Node1
    	  ATTRIBUTE: nodeId='1'
    	  TEXT: First Node
    	 ELEMENT: Node2
    	  ATTRIBUTE: nodeId='2'
    	  TEXT: Second Node
    	 ELEMENT: Node3
    	  ATTRIBUTE: nodeId='3'
    	  TEXT: Third Node

#### Discussion

Reading existing XML and identifying different node types is one of the fundamental actions that you will need to perform when dealing with XML. The code in the Solution creates an `XmlReader` from a string (it could also have used a stream), then iterates over the nodes while re-creating the formatted XML for output to the console window.

The Solution shows creating a `MemoryStream` from an XML fragment in a string like this:

    	    string xmlFragment = "<?xml version='1.0'?>" +
    	        "<!-- My sample XML -->" +
    	        "<?pi myProcessingInstruction?>" +
    	        "<Root>" +
    	        "<Node1 nodeId='1'>First Node</Node1>" +
    	        "<Node2 nodeId='2'>Second Node</Node2>" +
    	        "<Node3 nodeId='3'>Third Node</Node3>" +
    	        "</Root>";

    	    byte[] bytes = Encoding.UTF8.GetBytes(xmlFragment);
    	    MemoryStream memStream = new MemoryStream(bytes);

Once the `MemoryStream` has been established, the settings for the `XmlReader` need to be set up on an `XmlReaderSettings` object instance. These settings tell the `XmlReader` to check for any illegal characters in the XML fragment:

    	    XmlReaderSettings settings = new XmlReaderSettings();
    	    // Check for any illegal characters in the XML.
    	    settings.CheckCharacters = true;

The `while` loop iterates over the XML by reading one node at a time and examining the `NodeType` property of the current node that the reader is on to determine what type of XML node it is:

    	    while (reader.Read( ))
    	    {
    	        switch (reader.NodeType)
    	        {

The `NodeType` property is an `XmlNodeType` enumeration value that specifies the types of XML nodes that can be present. The `XmlNodeType` enumeration values are shown in Table 15-1.

##### Table 15-1. The XmlNodeType enumeration values

Name | Description
--- | ---
`Attribute` | An attribute node of an element.
`CDATA` | A marker for sections of text to escape that would usually be treated as markup.
`Comment` | A comment in the XML: `<!my comment -->`.
`Document` | The root of the XML document tree.
`DocumentFragment` | Document fragment node.
`DocumentType` | The document type declaration.
`Element` | An element tag: `<myelement>`.
`EndElement` | An end element tag: `</myelement>`.
`EndEntity` | Returned at the end of an entity after calling ResolveEntity.
`Entity` | Entity declaration.
`EntityReference` | A reference to an entity.
`None` | This is the node returned if `Read` has not yet been called on the `XmlReader`.
`Notation` | A notation in the DTD (document type definition).
`ProcessingInstruction` | The processing instruction: `<?pi myProcessingInstruction?>`.
`SignificantWhitespace` | Whitespace when mixed content model is used or when whitespace is being preserved.
`Text` | Text content for a node.
`Whitespace` | The whitespace between markup entries.
`XmlDeclaration` | The first node in the document that cannot have children: <`?xml version='1.0'?>`.

#### See Also

See the "XmlReader Class," "XmlNodeType Enumeration," and "MemoryStream Class" topics in the MSDN documentation.

### Recipe 15.2. Reading XML on the Web

#### Problem

Given a URL that points to an XML document, you need to grab the XML.

#### Solution

Use the `XmlReader` constructor that takes a URL as a parameter:

    	string url = "http://localhost/xml/sample.xml";
    	using (XmlReader reader = XmlReader.Create(url))
    	{
    	    while (reader.Read())
    	    {
    	        switch (reader.NodeType)
    	        {
    	            case XmlNodeType.Element:
    	                Console.Write("<{0}>", reader.Name);
    	                break;
    	        }
    	    }
    	}

#### Discussion

Using the `XmlReader.Create` method with a URI is a quick way to access XML documents that are stored remotely without writing all of the connectivity code. This uses an instance of the `XmlUrlResolver` class to check the URI passed in and then opens a stream to the XML document indicated by the URI. To specify settings on the reader, there is a second overload of `Create` that also takes an `XmlReaderSettings` instance to facilitate this.

The sample.xml file being referenced in this code is set up in a virtual directory named xml on the local system. The code retrieves the sample.xml file from the web server and displays all of the elements in the XML.

sample.xml contains the following XML data:

    	<?xml version='1.0'?>
    	<!-- My sample XML -->
    	<?pi myProcessingInstruction?>
    	<Root>
    	    <Node1 nodeId='1'>First Node</Node1>
    	    <Node2 nodeId='2'>Second Node</Node2>
    	    <Node3 nodeId='3'>Third Node</Node3>
    	    <Node4><![CDATA[<>\&']]></Node4>
    	</Root>

#### See Also

See the "XmlReader Class" topic in the MSDN documentation.

### Recipe 15.3. Querying the Contents of an XML Document

#### Problem

You have a large and complex XML document and you need to find various pieces of information, such as all the information contained within a specific element and having a particular attribute setting. You want to query the XML structure without having to iterate through all the nodes in the XML document and search for a particular item by hand.

#### Solution

To query a database, you normally use SQL. To query an XML document, you currently use `XPath` or with SQL Server 2005 possibly `XQuery` (as .NET 2.0 does not support it as part of the Base Class Library [BCL.NET]). In .NET, this means using the `System.Xml.XPath` namespace and classes like `XPathDocument`, `XPathNavigator`, and `XPathNodeIterator`.

In the following example, you use these classes to select nodes from an XML document that holds members from the board game Clue (or Cluedo, as it is known abroad) and their various roles. You want to be able to select the married female participants who were witnesses to the crime. In order to do this, pass an `XPath` expression to query the XML dataset as shown in Example 15-2.

##### Example 15-2. Querying an XML document

    public static void QueryXML( )
    {
        string xmlFragment = "<?xml version='1.0'?>" +
            "<Clue>" +
            "<Participant type='Perpetrator'>Professor Plum</Participant>" +
            "<Participant type='Witness'>Colonel Mustard</Participant>" +
            "<Participant type='Witness'>Mrs. White</Participant>" +
            "<Participant type='Witness'>Mrs. Peacock</Participant>" +
            "<Participant type='Witness'>Mr. Green</Participant>" +
            "</Clue>";

        using (StringReader reader = new StringReader(xmlFragment))
        {
            // Instantiate an XPathDocument using the StringReader.
            XPathDocument xpathDoc = new XPathDocument(reader);

            // Get the navigator.
            XPathNavigator xpathNav = xpathDoc.CreateNavigator();

            // Get up the query looking for the married female participants
            // who were witnesses.
            string xpathQuery =
                "/Clue/Participant[attribute::type='Witness'][contains(text(),'Mrs.')]";
            XPathExpression xpathExpr = xpathNav.Compile(xpathQuery);

            // Get the nodeset from the compiled expression.
            XPathNodeIterator xpathIter = xpathNav.Select(xpathExpr);

            // Write out the nodes found (Mrs. White and Mrs.Peacock in this instance).
            while (xpathIter.MoveNext())
            {
                Console.WriteLine(xpathIter.Current.Value);
            }
        }
    }

This outputs the following:

    	Mrs. White
    	Mrs. Peacock

#### Discussion

`XPath` is a very versatile language for performing queries on XML-based data. In order to accomplish this goal, you first create an XML fragment that looks like this:

    	<?xml version='1.0'?>
    	<Clue>
    	    <Participant type='Perpetrator'>Professor Plum</Participant>
    	    <Participant type='Witness'>Colonel Mustard</Participant>
    	    <Participant type='Witness'>Mrs. White</Participant>
    	    <Participant type='Witness'>Mrs. Peacock</Participant>
    	    <Participant type='Witness'>Mr. Green</Participant>
    	</Clue>;

You then load this fragment into a `StringReader`, then construct an `XPathDocument` to allow you to create an `XPathNavigator`. This lets you use `XPath` syntax to query the XML document shown in the preceding listing. The `XPathDocument` instance wraps the `MemoryStream` so you can use `XPath` to locate nodes (as well as perform XSLT transforms directly), and the `XPathNavigator` gets the set of nodes selected by the `XPath` expression.

    	byte[] bytes = Encoding.UTF8.GetBytes(xmlFragment);
    	MemoryStream memStream = new MemoryStream(bytes);

    	// Instantiate an XPathDocument using the MemoryStream.
    	XPathDocument xpathDoc = new XPathDocument(memStream);

    	// Get the navigator.
    	XPathNavigator xpathNav = xpathDoc.CreateNavigator();

Now you have to determine the `XPath`-based query to get all of the married female participants who were witnesses. This is set up in the `xpathQuery` string like this:

    	// Get up the query looking for the married female participants
    	// who were witnesses.
    	string xpathQuery =
    	  "/Clue/Participant[attribute::type='Witness'][contains(text( ),'Mrs.')]";

To help you grasp what is going on here, let's look at the syntax:

- /`Clue/Participant` says "Get all of the Participants under the root-level node Clue."

- `Participant[attribute::type='Witness']` says "Select only Participants with an attribute called type with a value of Witness."

- `Participant[contains(text( ),'Mrs.')]` says "Select only Participants with a value that contains 'Mrs.'"

Put them all together and you get all of the married female participants who were witnesses.

Once you have an `XPathNavigator`, call the `Select` method on it, passing the `XPath-`based query to select the nodes you are looking for. These are returned via the `XPathNodeIterator`. You use the `XPathNodeIterator` to write out the names of the participants you found and close the `MemoryStream`.

#### See Also

See the "XPathDocument Class," "XPathNavigator Enumeration," and "XPathNode-Iterator Class" topics in the MSDN documentation.

### Recipe 15.4. Validating XML

#### Problem

You are accepting an XML document created by another source and you want to verify that it conforms to a specific schema. This schema may be in the form of an XML schema (XSD or XMLXDR); alternatively, you want the flexibility to use a document type definition (DTD) to validate the XML.

#### Solution

Use the `XmlReaderSettings` to create an `XmlReader` that can validate XML documents against any descriptor document, such as an XSD, a DTD, or an XDR, as shown in Example 15-3.

##### Example 15-3. Validating XML

    public static void ValidateXml()
    {
        // Create XSD schema collection with book.xsd.
        XmlReaderSettings settings = new XmlReaderSettings();
        // Wire up handler to get any validation errors.
        settings.ValidationEventHandler += settings_ValidationEventHandler;

        // Set the validation type to schema.
        settings.ValidationType = ValidationType.Schema;

        // Add book.xsd.
        settings.Schemas.Add(null, XmlReader.Create(@"..\..\Book.xsd"));
        // Make sure we added.
        if (settings.Schemas.Count > 0)
        {
            // Open the bookbad.xml file.
            using (XmlReader reader = XmlReader.Create(@"..\..\BookBad.xml", settings))
            {
                // Replace validReader with reader for the whole loop.
                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.Element)
                    {
                        Console.Write("<{0}", reader.Name);
                        while (reader.MoveToNextAttribute())
                        {
                            Console.Write(" {0}='{1}'", reader.Name,
                                reader.Value);
                        }
                        Console.Write(">");
                    }
                    else if (reader.NodeType == XmlNodeType.Text)
                    {
                        Console.Write(reader.Value);
                    }
                    else if (reader.NodeType == XmlNodeType.EndElement)
                    {
                        Console.WriteLine("</{0}>", reader.Name);
                    }
                }
            }
        }
    }

    private static void settings_ValidationEventHandler(object sender,
    	                            ValidationEventArgs e)
    {
    	Console.WriteLine("Validation Error Message: {0}", e.Message);
    	Console.WriteLine("Validation Error Severity: {0}", e.Severity);
    	if (e.Exception != null)
    	{
    	    Console.WriteLine("Validation Error Line Number: {0}",
    	            e.Exception.LineNumber);
    	    Console.WriteLine("Validation Error Line Position: {0}",
    	            e.Exception.LinePosition);
    	    Console.WriteLine("Validation Error Source: {0}",
    	            e.Exception.Source);
    	    Console.WriteLine("Validation Error Source Schema: {0}",
    	            e.Exception.SourceSchemaObject);
    	    Console.WriteLine("Validation Error Source Uri: {0}",
    	            e.Exception.SourceUri);
    	    Console.WriteLine("Validation Error thrown from: {0}",
    	            e.Exception.TargetSite);
    	    Console.WriteLine("Validation Error callstack: {0}",
    	            e.Exception.StackTrace);
    	}
    }

#### Discussion

The Solution illustrates how to use the `XmlReader` to validate the book.xml document against a book.xsd XSD definition file. DTDs were the original way to specify the structure of an XML document, but it has become more common to use XSD since it reached W3C Recommendation status in May 2001. XDR was a predecessor of XSD provided by Microsoft, and, while it might be encountered in existing systems, it should not be used for new development.

The first thing to do is create an `XmlReader` to hold your XSD (book.xsd). Add it to the `XmlSchemaSet` (`Schemas` property) on the `XmlReaderSettings` object `settings`:

    	// Wire up handler to get any validation errors.
    	settings.ValidationEventHandler += settings_ValidationEventHandler;

    	// Set the validation type to schema.
    	settings.ValidationType = ValidationType.Schema;

    	// Add book.xsd.
    	settings.Schemas.Add(null, XmlReader.Create(@"..\..\Book.xsd"));

The preceding code also hooks up the schema-collection event handler for validation errors to the `settings_ValidationEventHandler` function. It also sets the `ValidationType` to `Schema`. Setting `XmlReaderSettings.ValidationType` to `ValidationType. Schema` tells the `XmlReader` to perform XML Schema validation.

| To perform DTD validation, use a DTD and `ValidationType.DTD`, and to perform XDR validation, use an XDR schema and `ValidationType.XDR`.

The `settings_ValidationEventHandler` function then examines the `ValidationEventArgs` object passed when a validation error occurs and writes the pertinent information to the console:

    	private static void settings_ValidationEventHandler(object sender,
    	                                ValidationEventArgs e)
    	{
    	    Console.WriteLine("Validation Error Message: {0}", e.Message);
    	    Console.WriteLine("Validation Error Severity: {0}", e.Severity);
    	    if (e.Exception != null)
    	    {
    	        Console.WriteLine("Validation Error Line Number: {0}",
    	                e.Exception.LineNumber);
    	        Console.WriteLine("Validation Error Line Position: {0}",
    	                e.Exception.LinePosition);
    	        Console.WriteLine("Validation Error Source: {0}",
    	                e.Exception.Source);
    	        Console.WriteLine("Validation Error Source Schema: {0}",
    	                e.Exception.SourceSchemaObject);
    	        Console.WriteLine("Validation Error Source Uri: {0}",
    	                e.Exception.SourceUri);
    	        Console.WriteLine("Validation Error thrown from: {0}",
    	                e.Exception.TargetSite);
    	        Console.WriteLine("Validation Error callstack: {0}",
    	                e.Exception.StackTrace);
    	    }
    	}

Once you have the schema collection, create an `XmlReader` to load the BookBad.xml file:

    	// Open the book.xml file.
    	using(XmlReader reader = XmlReader.Create(@"..\..\BookBad.xml", settings))

You then proceed to roll over the XML document and write out the elements and attributes:

    	// Read all nodes and print out.
    	while (reader.Read())
    	{
    	    if(reader.NodeType == XmlNodeType.Element)
    	    {
    	        Console.Write("<{0}", reader.Name);
    	        while (reader.MoveToNextAttribute())
    	        {
    	            Console.Write(" {0}='{1}'", reader.Name,
    	                        reader.Value);
    	        }
    	        Console.Write(">");
    	    }
    	    else if (reader.NodeType == XmlNodeType.Text)
    	    {
    	        Console.Write(reader.Value);
    	    }
    	    else if (reader.NodeType == XmlNodeType.EndElement)
    	    {
    	        Console.WriteLine("</{0}>", reader.Name);
    	    }
    	}

The BookBad.xml file contains the following:

    	<?xml version="1.0" encoding="utf-8"?>
    	<Book xmlns="http://tempuri.org/Book.xsd" name="C# Cookbook">
    	    <Chapter>File System IO</Chapter>
    	    <Chapter>Security</Chapter>
    	    <Chapter>Data Structures and Algorithms</Chapter>
    	    <Chapter>Reflection</Chapter>
    	    <Chapter>Threading and Synchronization</Chapter>
    	    <Chapter>Numbers and Enumerations</Chapter>
    	    <BadElement>I don't belong here</BadElement>
    	    <Chapter>Strings and Characters</Chapter>
    	    <Chapter>Classes And Structures</Chapter>
    	    <Chapter>Collections</Chapter>
    	    <Chapter>XML</Chapter>
    	    <Chapter>Delegates, Events, and Anonymous Methods</Chapter>
    	    <Chapter>Diagnostics</Chapter>
    	    <Chapter>Toolbox</Chapter>
    	    <Chapter>Unsafe Code</Chapter>
    	    <Chapter>Regular Expressions</Chapter>
    	    <Chapter>Generics</Chapter>
    	    <Chapter>Iterators and Partial Types</Chapter>
    	    <Chapter>Exception Handling</Chapter>
    	    <Chapter>Web</Chapter>
    	    <Chapter>Networking</Chapter>
    	</Book>

The book.xsd file contains the following:

    	<?xml version="1.0" ?>
    	<xs:schema id="NewDataSet" targetNamespace="http://tempuri.org/Book.xsd"
    	xmlns:mstns="http://tempuri.org/Book.xsd"
    	    xmlns="http://tempuri.org/Book.xsd"
    	    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    	    xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
    	    attributeFormDefault="qualified" elementFormDefault="qualified">
    	    <xs:element name="Book">
    	        <xs:complexType>
    	            <xs:sequence>
    	                <xs:element name="Chapter" nillable="true"
    	                               minOccurs="0" maxOccurs="unbounded">
    	                    <xs:complexType>
    	                        <xs:simpleContent
    	                           msdata:ColumnName="Chapter_Text" msdata:Ordinal="0">
    	                            <xs:extension base="xs:string">
    	                            </xs:extension>
    	                        </xs:simpleContent>
    	                    </xs:complexType>
    	                </xs:element>
    	            </xs:sequence>
    	            <xs:attribute name="name" form="unqualified" type="xs:string"/>
    	        </xs:complexType>
    	    </xs:element>
    	</xs:schema>

When this is run, the following output is generated, showing the validation failure occurring on `BadElement`:

    	<Book xmlns='http://tempuri.org/Book.xsd' name='C# Cookbook'><Chapter>File System
    	IO</Chapter>
    	<Chapter>Security</Chapter>
    	<Chapter>Data Structures and Algorithms</Chapter>
    	<Chapter>Reflection</Chapter>
    	<Chapter>Threading and Synchronization</Chapter>
    	<Chapter>Numbers and Enumerations</Chapter>
    	Validation Error Message: The element 'Book' in namespace 'http://tempuri.org/Book.
    	xsd' has invalid child element 'BadElement' in namespace 'http://tempuri.org/Book.
    	xsd'. List of possible elements expected: 'Chapter' in namespace 'http://tempuri.org/
    	Book.xsd'.
    	Validation Error Severity: Error
    	Validation Error Line Number: 9
    	Validation Error Line Position: 6
    	Validation Error Source:
    	Validation Error Source Schema:
    	Validation Error Source Uri: file:///C:/PRJ32/Book_2_0/C%23Cookbook2/Code/
    	CSharpRecipes/BookBad.xml
    	Validation Error thrown from:
    	Validation Error callstack:
    	<BadElement>I don't belong here</BadElement>
    	<Chapter>Strings and Characters</Chapter>
    	<Chapter>Classes And Structures</Chapter>
    	<Chapter>Collections</Chapter>
    	<Chapter>XML</Chapter>
    	<Chapter>Delegates, Events, and Anonymous Methods</Chapter>
    	<Chapter>Diagnostics</Chapter>
    	<Chapter>Toolbox</Chapter>
    	<Chapter>Unsafe Code</Chapter>
    	<Chapter>Regular Expressions</Chapter>
    	<Chapter>Generics</Chapter>
    	<Chapter>Iterators and Partial Types</Chapter>
    	<Chapter>Exception Handling</Chapter>
    	<Chapter>Web</Chapter>
    	<Chapter>Networking</Chapter>
    	</Book>

#### See Also

See the "XmlReader Class," "XmlSchemaSet Class," "ValidationEventHandler Class," "ValidationType Enumeration," and "XmlReaderSettings Class" topics in the MSDN documentation.

### Recipe 15.5. Creating an XML Document Programmatically

#### Problem

You have data that you want to put into a more structured form, such as an XML document.

#### Solution

Suppose you have the information shown in Table 15-2 for an address book that you want to turn into XML.

##### Table 15-2. Sample address book data

Name | Phone
--- | ---
Tim | 999-888-0000
Newman | 666-666-6666
Harold | 777-555-3333

Use the `XmlWriter` to create XML for this table:

    	    XmlWriterSettings settings = new XmlWriterSettings();
    	    settings.Indent = true;
    	    using (XmlWriter writer = XmlWriter.Create(Console.Out, settings))
    	    {
    	        writer.WriteStartElement("AddressBook");
    	        writer.WriteStartElement("Contact");
    	        writer.WriteAttributeString("name", "Tim");
    	        writer.WriteAttributeString("phone", "999-888-0000");
    	        writer.WriteEndElement();
    	        writer.WriteStartElement("Contact");
    	        writer.WriteAttributeString("name", "Newman");
    	        writer.WriteAttributeString("phone", "666-666-6666");
    	        writer.WriteEndElement();
    	        writer.WriteStartElement("Contact");
    	        writer.WriteAttributeString("name", "Harold");
    	        writer.WriteAttributeString("phone", "777-555-3333");
    	        writer.WriteEndElement();
    	        writer.WriteEndElement();
    	    }

This method will give you output like this:

    	<AddressBook>
    	    <Contact name="Tim" phone="999-888-0000" />
    	    <Contact name="Newman" phone="666-666-6666" />
    	    <Contact name="Harold" phone="777-555-3333" />
    	</AddressBook>

Or you can use the `XmlDocument` class to programmatically construct the XML:

    	public static void CreateXml( )
    	{
    	    // Start by making an XmlDocument.
    	    XmlDocument xmlDoc = new XmlDocument( );
    	    // Create a root node for the document.
    	    XmlElement addrBook = xmlDoc.CreateElement("AddressBook");
    	    xmlDoc.AppendChild(addrBook);
    	    // Create the Tim contact.
    	    XmlElement contact = xmlDoc.CreateElement("Contact");
    	    contact.SetAttribute("name","Tim");
    	    contact.SetAttribute("phone","999-888-0000");
    	    addrBook.AppendChild(contact);
    	    // Create the Newman contact.
    	    contact = xmlDoc.CreateElement("Contact");
    	    contact.SetAttribute("name","Newman");
    	    contact.SetAttribute("phone","666-666-6666");
    	    addrBook.AppendChild(contact);
    	    // Create the Harold contact.
    	    contact = xmlDoc.CreateElement("Contact");
    	    contact.SetAttribute("name","Harold");
    	    contact.SetAttribute("phone","777-555-3333");
    	    addrBook.AppendChild(contact);
    	    // Display XML.
    	    Console.WriteLine("Generated XML:\r\n{0}",addrBook.OuterXml);
    	    Console.WriteLine( );
    	}

This method gives the output like this:

    	Generated XML:
    	<AddressBook><Contact name="Tim" phone="999-888-0000" /><Contact name="Newman"
    	phone="666-666-6666" /><Contact name="Harold" phone="777-555-3333" /></AddressBook>

Both methods produce the same XML, but the first method is formatted with indents.

#### Discussion

Now that you have seen two ways to do this, the question arises: "Which one to use?" The `XmlDocument` uses the traditional DOM method of interacting with XML, while the `XmlReader/XmlWriter` combination deals with XML in a stream. If you are dealing with larger documents, you are probably better off using the `XmlReader/XmlWriter` combination than the `XmlDocument`. The `XmlReader/XmlWriter` combination is the better-performing of the two when you do not need the whole document in memory. If you need the power of being able to traverse back over what you have written already or write items out of order, use `XmlDocument`.

`XmlDocument` is the class that implements the DOM model for XML processing in the .NET Framework. The DOM holds all of the nodes in the XML in memory at the same time, which enables tree traversal both forward and backward. DOM also allows for a writable interface to the whole XML document, which other XML classes do not provide in .NET. `XmlDocument` allows you to manipulate any aspect of the XML tree. It is also eligible to be used for XSLT transformations via the `XslCompiledTransform` class, through its support of the `IXPathNavigable` interface. It allows you to run `XPath` queries against the document without having to create an `XPathDocument` first.

#### See Also

See the "XmlDocument Class," "XML Document Object Model (DOM)," "XslCompiledTransform Class," and "IXPathNavigable Interface" topics in the MSDN documentation.

### Recipe 15.6. Detecting Changes to an XML Document

#### Problem

You need to inform one or more classes or components that a node in an XML document has been inserted or removed or had its value changed.

#### Solution

In order to track changes to an active XML document, subscribe to the events published by the `XmlDocument` class. `XmlDocument` publishes events for node creation, insertion, and removal for both the pre- and post-conditions of these actions.

Example 15-4 shows a number of event handlers defined in the same scope as the `DetectXMLChanges` method, but they could just as easily be callbacks to functions on other classes that are interested in the manipulation of the live XML document.

`DetectXMLChanges` loads an XML fragment you define in the method; wires up the event handlers for the node events; adds, changes, and removes some nodes to trigger the events; then writes out the resulting XML.

##### Example 15-4. Detecting changes to an XML document

    public static void DetectXmlChanges( )
    {
        string xmlFragment = "<?xml version='1.0'?>" +
            "<!-- My sample XML -->" +
            "<?pi myProcessingInstruction?>" +
            "<Root>" +
            "<Node1 nodeId='1'>First Node</Node1>" +
            "<Node2 nodeId='2'>Second Node</Node2>" +
            "<Node3 nodeId='3'>Third Node</Node3>" +
            @"<Node4><![CDATA[<>\&']]></Node4>" +
            "</Root>";

        XmlDocument doc = new XmlDocument( );
        doc.LoadXml(xmlFragment);

        //Create the event handlers.
        doc.NodeChanging += new XmlNodeChangedEventHandler(NodeChangingEvent);
        doc.NodeChanged += new XmlNodeChangedEventHandler(NodeChangedEvent);
        doc.NodeInserting += new XmlNodeChangedEventHandler(NodeInsertingEvent);
        doc.NodeInserted += new XmlNodeChangedEventHandler(NodeInsertedEvent);
        doc.NodeRemoving += new XmlNodeChangedEventHandler(NodeRemovingEvent);
        doc.NodeRemoved += new XmlNodeChangedEventHandler(NodeRemovedEvent);

        // Add a new element node.
        XmlElement elem = doc.CreateElement("Node5");
        XmlText text = doc.CreateTextNode("Fifth Element");
        doc.DocumentElement.AppendChild(elem);
        doc.DocumentElement.LastChild.AppendChild(text);

        // Change the first node.
        doc.DocumentElement.FirstChild.InnerText = "1st Node";

        // Remove the fourth node.
        XmlNodeList nodes = doc.DocumentElement.ChildNodes;
        foreach(XmlNode node in nodes)
        {

            if(node.Name == "Node4")
            {
                doc.DocumentElement.RemoveChild(node);
                break;
            }
        }

        // Write out the new xml.
        Console.WriteLine(doc.OuterXml);
    }

Example 15-5 shows the event handlers from the `XmlDocument`, along with one formatting method, `WriteNodeInfo`. This method takes an action string and gets the name and value of the node being manipulated. All of the event handlers invoke this formatting method, passing the corresponding action string.

##### Example 15-5. XMLDocument event handlers and WtiteNodeInfo method

    private static void WriteNodeInfo(string action, XmlNode node)
    {
        if (node.Value != null)
        {
            Console.WriteLine("Element: <{0}> {1} with value {2}",
                node.Name,action,node.Value);
        }
        else
            Console.WriteLine("Element: <{0}> {1} with null value",
                node.Name,action);
    }

    public static void NodeChangingEvent(object source, XmlNodeChangedEventArgs e)
    {
        WriteNodeInfo("changing",e.Node);
    }

    public static void NodeChangedEvent(object source, XmlNodeChangedEventArgs e)
    {
        WriteNodeInfo("changed",e.Node);
    }

    public static void NodeInsertingEvent(object source, XmlNodeChangedEventArgs e)
    {
        WriteNodeInfo("inserting",e.Node);
    }

    public static void NodeInsertedEvent(object source, XmlNodeChangedEventArgs e)
    {
        WriteNodeInfo("inserted",e.Node);
    }

    public static void NodeRemovingEvent(object source, XmlNodeChangedEventArgs e)
    {
        WriteNodeInfo("removing",e.Node);
    }

    public static void NodeRemovedEvent(object source, XmlNodeChangedEventArgs e)
    {
        WriteNodeInfo("removed",e.Node);
    }

The `DetectXmlChanges` method results in the following output:

    	Element: <Node5> inserting with null value
    	Element: <Node5> inserted with null value
    	Element: <#text> inserting with value Fifth Element
    	Element: <#text> inserted with value Fifth Element
    	Element: <#text> changing with value First Node
    	Element: <#text> changed with value 1st Node
    	Element: <Node4> removing with null value
    	Element: <Node4> removed with null value
    	<?xml version="1.0"?><!-- My sample XML --><?pi myProcessingInstruction?><Root><
    	Node1 nodeId="1">1st Node</Node1><Node2 nodeId="2">Second Node</Node2><Node3 nod
    	eId="3">Third Node</Node3><Node5>Fifth Elementy/Node5></Root>

#### Discussion

With an `XmlDocument`, you can traverse both forward and backward in the XML stream, as well as use `XPath` navigation to find nodes. If you are just reading XML and not modifying it, and you have no need for traversing backward through the nodes, you should avoid using `XmlDocument`, since `XmlReader` is faster for reading and `XmlWriter` is faster for writing (both have less overhead than `XmlDocument`). The .NET Framework team did a nice job of giving XML processing flexibility, but if you use a class with more functionality than you need, you will pay the resulting performance penalty.

#### See Also

See the "XmlDocument Class" and "XmlNodeChangedEventHandler Class" topics in the MSDN documentation.

### Recipe 15.7. Handling Invalid Characters in an XML String

#### Problem

You are creating an XML string. Before adding a tag containing a text element, you want to check it to determine whether the string contains any of the following invalid characters:

    	<
    	>
    	"
    	'
    	&

If any of these characters are encountered, you want them to be replaced with their escaped form:

    	&lt;
    	&gt;
    	&quot;
    	&apos;
    	&amp;

#### Solution

There are different ways to accomplish this, depending on which XML-creation approach you are using. If you are using `XmlWriter`, the `WriteCData`, `WriteString`, `WriteAttributeString`, `WriteValue`, and `WriteElementString` methods take care of this for you. If you are using `XmlDocument` and `XmlElements`, the `XmlElement.InnerText` method will handle these characters.

The two ways to handle this using an `XmlWriter` work like this. The `WriteCData` method will wrap the invalid character text in a `CDATA` section, as shown in the creation of the `InvalidChars1` element in the example that follows. The other method, using `XmlWriter`, is to use the `WriteElementString` method that will automatically escape the text for you, as shown while creating the `InvalidChars2` element.

    	// Set up a string with our invalid chars.
    	string invalidChars = @"<>\&'";
    	XmlWriterSettings settings = new XmlWriterSettings();
    	settings.Indent = true;
    	using (XmlWriter writer = XmlWriter.Create(Console.Out, settings))
    	{
    	    writer.WriteStartElement("Root");
    	    writer.WriteStartElement("InvalidChars1");
    	    writer.WriteCData(invalidChars);
    	    writer.WriteEndElement();
    	    writer.WriteElementString("InvalidChars2", invalidChars);
    	    writer.WriteEndElement();
    	}

The output from this is:

    	<?xml version="1.0" encoding="IBM437"?>
    	<Root>
    	    <InvalidChars1><![CDATA[<>\&']]></InvalidChars1>
    	    <InvalidChars2>&lt;&gt;\&amp;'</InvalidChars2>
    	</Root>

There are two ways you can handle this problem with `XmlDocument` and `XmlElement`. The first way is to surround the text you are adding to the XML element with a CDATA section and add it to the `InnerXML` property of the `XmlElement`:

    	// Set up a string with our invalid chars.
    	string invalidChars = @"<>\&'";
    	XmlElement invalidElement1 = xmlDoc.CreateElement("InvalidChars1");
    	invalidElement1.AppendChild(xmlDoc.CreateCDataSection(invalidChars));

The second way is to let the `XmlElement` class escape the data for you by assigning the text directly to the `InnerText` property like this:

    	// Set up a string with our invalid chars.
    	string invalidChars = @"<>\&'";
    	XmlElement invalidElement2 = xmlDoc.CreateElement("InvalidChars2");
    	invalidElement2.InnerText = invalidChars;

The whole `XmlDocument` is created with these `XmlElements` in this code:

    	public static void HandlingInvalidChars( )
    	{
    	    // Set up a string with our invalid chars.
    	    string invalidChars = @"<>\&'";

    	    XmlDocument xmlDoc = new XmlDocument( );
    	    // Create a root node for the document.
    	    XmlElement root = xmlDoc.CreateElement("Root");
    	    xmlDoc.AppendChild(root);

    	    // Create the first invalid character node.
    	    XmlElement invalidElement1 = xmlDoc.CreateElement("InvalidChars1");
    	    // Wrap the invalid chars in a CDATA section and use the
    	    // InnerXML property to assign the value as it doesn't
    	    // escape the values, just passes in the text provided.
    	    invalidElement1.InnerXml = "<![CDATA[" + invalidChars + "]]>";
    	    // Append the element to the root node.
    	    root.AppendChild(invalidElement1);

    	    // Create the second invalid character node.
    	    XmlElement invalidElement2 = xmlDoc.CreateElement("InvalidChars2");
    	    // Add the invalid chars directly using the InnerText
    	    // property to assign the value as it will automatically
    	    // escape the values.
    	    invalidElement2.InnerText = invalidChars;
    	    // Append the element to the root node.
    	    root.AppendChild(invalidElement2);

    	    Console.WriteLine("Generated XML with Invalid Chars:\r\n{0}",xmlDoc.OuterXml);
    	    Console.WriteLine( );
    	}

The XML created by this procedure (and output to the console) looks like this:

    	Generated XML with Invalid Chars:
    	<Root><InvalidChars1><![CDATA[<>\&']]></InvalidChars1><InvalidChars2>&lt;&gt;\
    	&amp;'</InvalidChars2></Root>

#### Discussion

The `CDATA` node allows you to represent the items in the text section as character data, not as escaped XML, for ease of entry. Normally these characters would need to be in their escaped format (`&lt;` for `<` and so on), but the `CDATA` section allows you to enter them as regular text.

When the `CDATA` tag is used in conjunction with the `InnerXml` property of the `XmlElement` class, you can submit characters that would normally need to be escaped first. The `XmlElement` class also has an `InnerText` property that will automatically escape any markup found in the string assigned. This allows you to add these characters without having to worry about them.

#### See Also

See the "XmlDocument Class," "XmlWriter Class," "XmlElement Class," and "CDATA Sections" topics in the MSDN documentation.

### Recipe 15.8. Transforming XML

#### Problem

You have a raw XML document that you need to convert into a more readable format. For example, you have personnel data that is stored as an XML document and you need to display it on a web page or place it in a comma-delimited text file for legacy system integration. Unfortunately, not everyone wants to sort through reams of XML all day; they would rather read the data as a formatted list or within a grid with defined columns and rows. You need a method of transposing the XML data into a more readable form as well as into the comma-delimited format.

#### Solution

The solution for this is to use an XSLT stylesheet to transform the XML into another format using the `XslCompiledTransform` class. In the example code, you transform some personnel data from a fictitious business stored in Personnel.xml. First, load the stylesheet for generating HTML output, then perform the transformation to HTML via XSLT using the PersonnelHTML.xsl stylesheet. After that, transform the data to comma-delimited format using the PersonnelCSV.xsl stylesheet:

    	public static void TransformXML()
    	{
    	    // Create a resolver with default credentials.
    	    XmlUrlResolver resolver = new XmlUrlResolver();
    	    resolver.Credentials = System.Net.CredentialCache.DefaultCredentials;

    	    // Transform the personnel.xml file to html.
    	    XslCompiledTransform transform = new XslCompiledTransform();
    	    XsltSettings settings = new XsltSettings();
    	    // Disable both of these (the default) for security reasons.
    	    settings.EnableDocumentFunction = false;
    	    settings.EnableScript = false;
    	    // Load up the stylesheet.
    	    transform.Load(@"..\..\PersonnelHTML.xsl",settings,resolver);
    	    // Perform the transformation.
    	    transform.Transform(@"..\..\Personnel.xml",@"..\..\Personnel.html");

    	    // Or transform the Personnel.xml file to comma-delimited format.

    	    // Load up the stylesheet.
    	    transform.Load(@"..\..\PersonnelCSV.xsl",settings,resolver);
    	    // Perform the transformation.
    	    transform.Transform(@"..\..\Personnel.xml",
    	        @"..\..\Personnel.csv");
    	}

The Personnel.xml file contains the following items:

    	<?xml version="1.0" encoding="utf-8"?>
    	<Personnel>
    	    <Employee name="Bob" title="Customer Service" companyYears="1"/>
    	    <Employee name="Alice" title="Manager" companyYears="12"/>
    	    <Employee name="Chas" title="Salesman" companyYears="3"/>
    	    <Employee name="Rutherford" title="CEO" companyYears="27"/>
    	</Personnel>

The PersonnelHTML.xsl stylesheet looks like this:

    	<?xml version="1.0" encoding="UTF-8"?>
    	<xsl:stylesheet version="1.0"
    	   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    	   xmlns:xs="http://www.w3.org/2001/XMLSchema">
    	   <xsl:template match="/">
    	     <html>
    	       <head />
    	         <body title="Personnel">
    	           <xsl:for-each select="Personnel">
    	             <p>
    	               <xsl:for-each select="Employee">
    	                 <xsl:if test="position( )=1">
    	                     <table border="1">
    	                         <thead>
    	                             <tr>
    	                               <td>Employee Name</td>
    	                               <td>Employee Title</td>
    	                               <td>Years with Company</td>
    	                             </tr>
    	                         </thead>
    	                         <tbody>
    	                           <xsl:for-each select="../Employee">
    	                             <tr>
    	                               <td>
    	                                 <xsl:for-each select="@name">
    	                                     <xsl:value-of select="." />
    	                                 </xsl:for-each>
    	                               </td>
    	                               <td>
    	                                 <xsl:for-each select="@title">
    	                                     <xsl:value-of select="." />
    	                                 </xsl:for-each>
    	                               </td>
    	                               <td>
    	                                 <xsl:for-each select="@companyYears">
    	                                     <xsl:value-of select="." />
    	                                 </xsl:for-each>
    	                               </td>
    	                             </tr>
    	                                  </xsl:for-each>
    	                                </tbody>
    	                              </table>
    	                         </xsl:if>
    	                    </xsl:for-each>
    	                  </p>
    	               </xsl:for-each>
    	         </body>
    	     </html>
    	   </xsl:template>
    	</xsl:stylesheet>

To generate the HTML screen in Figure 15-1, use the PersonnelHTML.xsl stylesheet and the Personnel.xml file.

##### Figure 15-1. Personnel HTML table generated from Personnel.xml

Here is the HTML source:

    	<html xmlns:xs="http://www.w3.org/2001/XMLSchema">
    	  <head>
    	    <META http-equiv="Content-Type" content="text/html; charset=utf-8">
    	  </head>
    	  <body title="Personnel">
    	    <p>
    	      <table border="1">
    	        <thead>
    	          <tr>
    	            <td>Employee Name</td>
    	            <td>Employee Title</td>
    	            <td>Years with Company</td>
    	          </tr>
    	        </thead>
    	        <tbody>
    	          <tr>
    	            <td>Bob</td>
    	            <td>Customer Service</td>
    	            <td>1</td>
    	          </tr>
    	          <tr>
    	            <td>Alice</td>
    	            <td>Manager</td>
    	            <td>12</td>
    	          </tr>
    	          <tr>
    	            <td>Chas</td>
    	            <td>Salesman</td>
    	            <td>3</td>
    	          </tr>
    	          <tr>
    	            <td>Rutherford</td>
    	            <td>CEO</td>
    	            <td>27</td>
    	          </tr>
    	        </tbody>
    	      </table>
    	    </p>
    	  </body>
    	</html>

To generate comma-delimited output, use PersonnelCSV.xsl and Personnel.xml; the stylesheet is shown here:

    	<?xml version="1.0" encoding="UTF-8"?>
    	<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    	xmlns:xs="http://www.w3.org/2001/XMLSchema">
    	<xsl:output method="text" encoding="UTF-8"/>
    	    <xsl:template match="/">
    	                   <xsl:for-each select="Personnel">
    	                       <xsl:for-each select="Employee">
    	                           <xsl:for-each select="@name">
    	                               <xsl:value-of select="." />
    	                           </xsl:for-each>,<xsl:for-each select="@title">
    	                               <xsl:value-of select="." />
    	                            </xsl:for-each>,<xsl:for-each select="@companyYears">
    	                                 <xsl:value-of select="." />
    	                            </xsl:for-each>
    	                   <xsl:text> &#xd;&#xa;</xsl:text>
    	                  </xsl:for-each>
    	                   </xsl:for-each>
    	    </xsl:template>
    	</xsl:stylesheet>

The output from the PersonnelCSV.xsl stylesheet is shown here:

    	Bob,Customer Service,1
    	Alice,Manager,12
    	Chas,Salesman,3
    	Rutherford,CEO,27

#### Discussion

There are many overrides for the `XslCompiledTransform.Transform` method. Since `XmlResolver` is an abstract class, you need to use either the `XmlUrlResolver` or the `XmlSecureResolver` or pass `null` as the `XmlResolver`-typed argument. The `XmlUrlResolver` will resolve URLs to external resources, such as schema files, using the FILE, HTTP, and HTTPS protocols. The `XmlSecureResolver` restricts the resources that you can access by requiring you to pass in evidence, which helps prevent cross-domain redirection in XML. If you are accepting XML from the Internet, it could easily have a redirection to a site where malicious XML would be waiting to be downloaded and executed if you were not using the `XmlSecureResolver`. If you pass `null` for the `XmlResolver`, you are saying you do not want to resolve any external resources. Microsoft has declared the `null` option to be obsolete, and it shouldn't be used anyway since you should always use some type of `XmlResolver`.

XSLT is a very powerful technology that allows you to transform XML into just about any format you can think of, but it can be frustrating at times. The simple need of a carriage return/line feed combination in the XSLT output was such a trial that we were able to find more than 20 different message board requests for help on how to do this! After looking at the W3C spec for XSLT, we found you could do this using the `xsl:text` element like this:

    	<xsl:text> &#xd;&#xa;</xsl:text>

The `&#xd;` stands for a hexadecimal 13, or a carriage return, and the `&#xa;` stands for a hexadecimal 10, or a line feed. This is output at the end of each employee's data from the XML.

#### See Also

See the "XslCompiledTransform Class," "XmlResolver Class," "XmlUrlResolver Class," "XmlSecureResolver Class," and "xsl:text" topics in the MSDN documentation.

### Recipe 15.9. Tearing Apart an XML Document

#### Problem

You have an XML document that needs to be broken apart into multiple parts. Each part can then be sent to a different destination (possibly a web service) to be processed individually. This solution is useful when you have a large document, such as an invoice, in XML form. For example, with an invoice, you would want to tear off the billing information and send this to Accounting, while sending the shipping information to Shipping, and then send the invoice items to Fulfillment to be processed.

#### Solution

In order to separate the invoice items, load an `XmlDocument` with the invoice XML from the Invoice.xml file shown in Example 15-6.

##### Example 15-6. Invoice.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <Invoice invoiceDate='2003-10-05' invoiceNumber='INV-01'>
        <shipInfo>
            <name>Beerly Standing</name>
            <attn>Receiving</attn>
            <street>47 South Street</street>
            <city>Intox</city>
            <state>NH</state>
        </shipInfo>
        <billInfo>
            <name>Beerly Standing</name>
            <attn>Accounting</attn>
            <street>98 North Street</street>
            <city>Intox</city>
            <state>NH</state>
        </billInfo>
        <Items>
            <item partNum="98745">
                <productName>Brown Eyed Stout</productName>
                <quantity>12</quantity>
                <price>23.99</price>
                <shipDate>2003-12-20</shipDate>
            </item>
            <item partNum="34987">
                <productName>Diamond Pearl Lager</productName>
                <quantity>22</quantity>
                <price>35.98</price>
                <shipDate>2003-12-20</shipDate>
            </item>
            <item partNum="AK254">
                <productName>Job Site Ale</productName>
                <quantity>50</quantity>
                <price>12.56</price>
                <shipDate>2003-11-12</shipDate>
            </item>
        </Items>
    </Invoice>

The code to tear this invoice apart and send the various information pieces to their respective departments is shown in Example 15-7.

##### Example 15-7. Tearing apart an XML document (Invoice.xml)

    public static void ProcessInvoice()
    {
        XmlDocument xmlDoc = new XmlDocument();
        // Pick up invoice from deposited directory.
        xmlDoc.Load(@"..\..\Invoice.xml");
        // Get the Invoice element node.
        XmlNode Invoice = xmlDoc.SelectSingleNode("/Invoice");

        // Get the invoice date attribute.
        XmlAttribute invDate =
            (XmlAttribute)Invoice.Attributes.GetNamedItem("invoiceDate");
        // Get the invoice number attribute.
        XmlAttribute invNum =
            (XmlAttribute)Invoice.Attributes.GetNamedItem("invoiceNumber");

        // Process the billing information to Accounting.
        WriteInformation(@"..\..\BillingEnvelope.xml",
                        "BillingEnvelope",
                        invDate, invNum, xmlDoc,
                        "/Invoice/billInfo");

        // Process the shipping information to Shipping.
        WriteInformation(@"..\..\ShippingEnvelope.xml",
                        "ShippingEnvelope",
                        invDate, invNum, xmlDoc,
                        "/Invoice/shipInfo");

        // Process the item information to Fulfillment.
        WriteInformation(@"..\..\FulfillmentEnvelope.xml",
                        "FulfillmentEnvelope",
                        invDate, invNum, xmlDoc,
                        "/Invoice/Items/item");

        // Now send the data to the web services …
    }

    private static void WriteInformation(string path,
                                string rootNode,
                                XmlAttribute invDate,
                                XmlAttribute invNum,
                                XmlDocument xmlDoc,
                                string nodePath)
    {
        XmlWriterSettings settings = new XmlWriterSettings();
        settings.Indent = true;
        using (XmlWriter writer =
            XmlWriter.Create(path, settings))
        {
            writer.WriteStartDocument();
            writer.WriteStartElement(rootNode);
            writer.WriteAttributeString(invDate.Name, invDate.Value);
            writer.WriteAttributeString(invNum.Name, invNum.Value);
            XmlNodeList nodeList = xmlDoc.SelectNodes(nodePath);
            // Add the billing information to the envelope.
            foreach (XmlNode node in nodeList)
            {
                writer.WriteRaw(node.OuterXml);
            }
            writer.WriteEndElement();
            writer.WriteEndDocument();
        }
    }

The "envelopes" containing the various pieces of XML data for the web services are listed below.

BillingEnvelope XML

      	<BillingEnvelope invoiceDate="2003-10-05" invoiceNumber="INV-01">
      	    <billInfo>
      	        <name>Beerly Standing</name>
      	        <attn>Accounting</attn>
      	        <street>98 North Street</street>
      	        <city>Intox</city>
      	        <state>NH</state>
      	    </billInfo>
      	</BillingEnvelope>

ShippingEnvelope XML

      	<ShippingEnvelope invoiceDate="2003-10-05" invoiceNumber="INV-01">
      	    <shipInfo>
      	        <name>Beerly Standing</name>
      	        <attn>Receiving</attn>
      	        <street>47 South Street</street>
      	        <city>Intox</city>
      	        <state>NH</statey>
      	    </shipInfo>
      	</ShippingEnvelope>

FulfillmentEnvelope XML

      	<FulfillmentEnvelope invoiceDate="2003-10-05" invoiceNumber="INV-01">
      	    <item partNum="98745">
      	        <productName>Brown Eyed Stout</productName>
      	        <quantity>12</quantity>
      	        <price>23.99</price>
      	        <shipDate>2003-12-20</shipDate>
      	    </item>
      	    <item partNum="34987">
      	        <productName>Diamond Pearl Lager</productName>
      	        <quantity>22</quantity>
      	        <price>35.98</price>
      	        <shipDate>2003-12-20</shipDate>
      	    </item>
      	    <item partNum="AK254">
      	        <productName>Job Site Ale</productName>
      	        <quantity>50</quantity>
      	        <price>12.56</price>
      	        <shipDate>2003-11-12</shipDate>
      	    </item>
      	</FulfillmentEnvelope>

#### Discussion

In order to tear apart the invoice, you need to establish what pieces go to which departments. The breakdown of this is that each of the envelopes gets the invoice date and invoice number from the main invoice to give context to the information in the envelope. The `billInfo` element and children go to the `BillingEnvelope`, the `shipInfo` element and children go to the `ShippingEnvelope`, and the `item` elements go to the `FulfillmentEnvelope`. Once these envelopes are constructed, they are sent to the web services for each department to perform its function for this invoice.

In the example program from the Solution, you first load the Invoice.xml file and get the attributes you are going to give to each of the envelopes:

    	XmlDocument xmlDoc = new XmlDocument( );
    	// Pick up invoice from deposited directory.
    	xmlDoc.Load(@"..\..\Invoice.xml");
    	// Get the Invoice element node.
    	XmlNode Invoice = xmlDoc.SelectSingleNode("/Invoice");

    	// Get the invoice date attribute.
    	XmlAttribute invDate =
    	    (XmlAttribute)Invoice.Attributes.GetNamedItem("invoiceDate");
    	// Get the invoice number attribute.
    	XmlAttribute invNum =
    	    (XmlAttribute)Invoice.Attributes.GetNamedItem("invoiceNumber");

Then you establish each envelope with the sections of the invoice that matter to the respective functions (the `BillingEnvelope` is handled by Accounting, the `ShippingEnvelope` is handled by Shipping, and the `FulfillmentEnvelope` is handled by `Fulfillment`) by calling the `WriteInformation` method, starting with the `BillingEnvelope`:

    	    // Process the billing information to Accounting.
    	    WriteInformation(@"..\..\BillingEnvelope.xml",
    	                    "BillingEnvelope",
    	                    invDate, invNum, xmlDoc,
    	                    "/Invoice/billInfo");

Then the `ShippingEnvelope` is created:

    	// Process the shipping information to Shipping.
    	WriteInformation(@"..\..\ShippingEnvelope.xml",
    	                    "ShippingEnvelope",
    	                    invDate, invNum, xmlDoc,
    	                    "/Invoice/shipInfo");

Finally, the `FulfillmentEnvelope` is created:

    	// Process the item information to Fulfillment.
    	WriteInformation(@"..\..\FulfillmentEnvelope.xml",
    	                    "FulfillmentEnvelope",
    	                    invDate, invNum, xmlDoc,
    	                    "/Invoice/Items/item");

At this point, each of the envelopes can be posted to the respective web services interfaces.

| When you append the attributes from the `Invoice` to the envelopes, you call the `XmlNode.Clone` method on the `XmlAttributes`. This is done so that each of the elements has its own separate copy. If you do not do this, then the attribute will appear only on the last element it is assigned to.

#### See Also

See the "XmlDocument Class," "XmlElement Class," and "XmlAttribute Class" topics in the MSDN documentation.

### Recipe 15.10. Putting Together an XML Document

#### Problem

You have various pieces of a document in XML form that need to be put together to form a single XML documentthe opposite of what was done in Recipe 15.9. In this case, you have received various pieces of an invoice in XML form. For example, one department sent the shipping information as an XML document, one sent the billing information in XML, and another sent invoice line items, also as an XML document. You need a way to put these XML pieces together to form a single XML invoice document.

#### Solution

In order to reconstitute the original invoice, you need to reverse the process used to create the pieces of the invoice using multiple `XmlDocuments`. There are three parts being sent back to you to help in re-forming the original invoice XML: `BillingEnvelope.xml`, `ShippingEnvelope.xml`, and `Fulfillment.xml`. These are listed below:

BillingEnvelope XML

      	<BillingEnvelope invoiceDate="2003-10-05" invoiceNumber="INV-01">
      	    <billInfo>
      	        <name>Beerly Standing</name>
      	        <attn>Accounting</attn>
      	        <street>98 North Street</street>
      	        <city>Intox</city>
      	        <state>NH</state>
      	    </billInfo>
      	</BillingEnvelope>

ShippingEnvelope XML

      	<ShippingEnvelope invoiceDate="2003-10-05" invoiceNumber="INV-01">
      	    <shipInfo>
      	        <name>Beerly Standing</name>
      	        <attn>Receiving</attn>
      	        <street>47 South Street</street>
      	        <city>Intox</city>
      	        <state>NH</state>
      	    </shipInfo>
      	</ShippingEnvelope>

FulfillmentEnvelope XML

      	<FulfillmentEnvelope invoiceDate="2003-10-05" invoiceNumber="INV-01">
      	    <item partNum="98745">
      	        <productName>Brown Eyed Stout</productName>
      	        <quantity>12</quantity>
      	        <price>23.99</price>
      	        <shipDate>2003-12-20</shipDate>
      	    </item>
      	    <item partNum="34987">
      	        <productName>Diamond Pearl Lager</productName>
      	        <quantity>22</quantity>
      	        <price>35.98</price>
      	        <shipDate>2003-12-20</shipDate>
      	    </item>
      	    <item partNum="AK254">
      	        <productName>Job Site Ale</productName>
      	        <quantity>50</quantity>
      	        <price>12.56</price>
      	        <shipDate>2003-11-12</shipDate>
      	    </item>
      	</FulfillmentEnvelope>

To put these back together as a single invoice, reverse the process you went through to break it apart, while inferring the invoice date and invoice number from the `BillingEnvelope` to help reestablish the invoice, as shown in Example 15-8.

##### Example 15-8. Reconstructing an XML document

    public static void ReceiveInvoice( )
    {
        XmlDocument invoice = new XmlDocument( );
        XmlDocument billing = new XmlDocument( );
        XmlDocument shipping = new XmlDocument( );
        XmlDocument fulfillment = new XmlDocument( );

        // Get up root invoice node.
        XmlElement invoiceElement = invoice.CreateElement("Invoice");
        invoice.AppendChild(invoiceElement);

        // Load the billing.
        billing.Load(@"..\..\BillingEnvelope.xml");
        // Get the invoice date attribute.
        XmlAttribute invDate = (XmlAttribute)
          billing.DocumentElement.Attributes.GetNamedItem("invoiceDate");
        // Get the invoice number attribute.
        XmlAttribute invNum = (XmlAttribute)
          billing.DocumentElement.Attributes.GetNamedItem("invoiceNumber");
        // Set up the invoice with this info.
        invoice.DocumentElement.Attributes.SetNamedItem(invDate.Clone( ));
        invoice.DocumentElement.Attributes.SetNamedItem(invNum.Clone( ));
        // Add the billInfo back in.
        XmlNodeList billList = billing.SelectNodes("/BillingEnvelope/billInfo");
        foreach(XmlNode billInfo in billList)
        {
            invoice.DocumentElement.AppendChild(invoice.ImportNode(billInfo,true));
        }

        // Load the shipping.
        shipping.Load(@"..\..\ShippingEnvelope.xml");
        // Add the shipInfo back in.
        XmlNodeList shipList = shipping.SelectNodes("/ShippingEnvelope/shipInfo");
        foreach(XmlNode shipInfo in shipList)
        {
            invoice.DocumentElement.AppendChild(invoice.ImportNode(shipInfo,true));
        }

        // Load the items.
        fulfillment.Load(@"..\..\FulfillmentEnvelope.xml");

        // Create an Items element in the Invoice to add these under.
        XmlElement items = invoice.CreateElement("Items");

        // Add the items back in under Items.
        XmlNodeList itemList = fulfillment.SelectNodes("/FulfillmentEnvelope/item");
        foreach(XmlNode item in itemList)
        {
            items.AppendChild(invoice.ImportNode(item,true));
        }

        // Add it in.
        invoice.DocumentElement.AppendChild(items.Clone( ));

        // Display Invoice XML.
        Console.WriteLine("Invoice:\r\n{0}",invoice.OuterXml);

        // Save our reconstitued invoice.
        invoice.Save(@"..\..\ReceivedInvoice.xml");
    }

The code reconstitutes the invoice and saves it as ReceivedInvoice.xml, the contents of which are shown here:

    	<Invoice invoiceDate="2003-10-05" invoiceNumber="INV-01"><billInfo><name>Beerly
    	Standing</name><attn>Accounting</attn><street>98 North Street</street><city>Intox</
    	city><state>NH</state></billInfo><shipInfo><name>Beerly Standing</name><attn>
    	Receiving</attn><street>47 South Street</street><city>Intox</city><state>NH</state></
    	shipInfo><Items><item partNum="98745"><productName>Brown Eyed Stout</productName>
    	<quantity>12</quantity><price>23.99</price><shipDate>2003-12-20</shipDate></item>
    	<item partNum="34987"><productName>Diamond Pearl Lager</productName><quantity>22</
    	quantity><price>35.98</price><shipDate>2003-12-20</shipDate></item><item
    	partNum="AK254"><productName>Job Site Ale</productName><quantity>50</quantity><price>
    	12.56</price><shipDate>2003-11-12</shipDate></item></Items></Invoice>

#### Discussion

In the Solution code, the first step is to create a set of `XmlDocuments` for the `Invoice, BillingEnvelope, ShippingEnvelope`, and `FulfillmentEnvelope`. Then you create the new root `Invoice` element in the invoice `XmlDocument`:

    	XmlDocument invoice = new XmlDocument( );
    	XmlDocument billing = new XmlDocument( );
    	XmlDocument shipping = new XmlDocument( );
    	XmlDocument fulfillment = new XmlDocument( );

    	// Set up root invoice node.
    	XmlElement invoiceElement = invoice.CreateElement("Invoice");
    	invoice.AppendChild(invoiceElement);

Next, you process the `BillingEnvelope`, taking the invoice date and number from it and adding it to the `Invoice`. Then you add the billing information back in to the invoice:

    	// Load the billing.
    	billing.Load(@"..\..\BillingEnvelope.xml");
    	// Get the invoice date attribute.
    	XmlAttribute invDate = (XmlAttribute)
    	 billing.DocumentElement.Attributes.GetNamedItem("invoiceDate");
    	// Get the invoice number attribute.
    	XmlAttribute invNum = (XmlAttribute)
    	 billing.DocumentElement.Attributes.GetNamedItem("invoiceNumber");
    	// Set up the invoice with this info.
    	invoice.DocumentElement.Attributes.SetNamedItem(invDate.Clone( ));
    	invoice.DocumentElement.Attributes.SetNamedItem(invNum.Clone( ));
    	// Add the billInfo back in.
    	XmlNodeList billList = billing.SelectNodes("/BillingEnvelope/billInfo");
    	foreach(XmlNode billInfo in billList)
    	{
    	    invoice.DocumentElement.AppendChild(invoice.ImportNode(billInfo,true));
    	}

The `ShippingEnvelope` came next:

    	// Load the shipping.
    	shipping.Load(@"..\..\ShippingEnvelope.xml");
    	// Add the shipInfo back in.
    	XmlNodeList shipList = shipping.SelectNodes("/ShippingEnvelope/shipInfo");
    	foreach(XmlNode shipInfo in shipList)
    	{
    	    invoice.DocumentElement.AppendChild(invoice.ImportNode(shipInfo,true));
    	}

And finally, the `items` from the `FulfillmentEnvelope` were placed back under an `Items` element under the main `Invoice` element:

    	// Load the items.
    	fulfillment.Load(@"..\..\FulfillmentEnvelope.xml");

    	// Create an Items element in the Invoice to add these under
    	XmlElement items = invoice.CreateElement("Items");

    	// Add the items back in under Items.
    	XmlNodeList itemList = fulfillment.SelectNodes("/FulfillmentEnvelope/item");
    	foreach(XmlNode item in itemList)
    	{
    	    items.AppendChild(invoice.ImportNode(item,true));
    	}

    	// Add it in.
    	invoice.DocumentElement.AppendChild(items.Clone( ));

One item to be aware of when dealing with multiple `XmlDocuments` is that when you take a node from one `XmlDocument`, you cannot just append it as a child to a node in a different `XmlDocument` because the node has the context of the original `XmlDocument`. If you try to do this, you will get the following exception message:

    	The node to be inserted is from a different document context.

To fix this, use the `XmlDocument.ImportNode` method, which will make a copy (deep or shallow) of the node you are bringing over to the new `XmlDocument`. For instance, when you add the shipping information like so:

    	invoice.DocumentElement.AppendChild(invoice.ImportNode(shipInfo,true));

this line takes the `shipInfo` node, clones it deeply, then appends it to the main invoice node.

#### See Also

See the "XmlDocument Class," "XmlElement Class," and "XmlAttribute Class" topics in the MSDN documentation.

### Recipe 15.11. Validating Modified XML Documents Without Reloading

#### Problem

You are using the `XmlDocument` to modify an XML document loaded in memory. Once the document has been modified, the modifications need to be verified and schema defaults need to be enforced.

#### Solution

Use the `XmlDocument.Validate` method to perform the validation and apply schema defaults and type information.

Create an `XmlSchemaSet` with the XML Schema document (book.xsd) and an `XmlReader`, and point the `Schemas` property of the `XmlDocument` to it:

    	string xmlFile = @"..\..\Book.xml";
    	string xsdFile = @"..\..\Book.xsd";

    	// Create the schema set.
    	XmlSchemaSet schemaSet = new XmlSchemaSet();
    	// Add the new schema with the target namespace
    	// (could add all the schema at once here if there are multiple).
    	schemaSet.Add("http://tempuri.org/Book.xsd", XmlReader.Create(xsdFile));

    	// Load up the XML file.
    	XmlDocument xmlDoc = new XmlDocument();
    	// Add the schema.
    	xmlDoc.Schemas = schemaSet;

Load the book.xml file into the `XmlDocument`, set up a `ValidationEventHandler` to catch any errors, then call `Validate` with the event handler to validate book.xml against the book.xsd schema:

    	// Validate after load.
    	xmlDoc.Load(xmlFile);
    	ValidationEventHandler eventHandler = ValidationEventHandler_15_11;
    	xmlDoc.Validate(eventHandler);

Add a new element node that is not in the schema into the `XmlDocument` and then call `Validate` again with the event handler to revalidate the changed `XmlDocument`. If the document triggers any validation events, then `bValidXml` is set to false by the `ValidationEventHandler`.

    	// Set the initial check for validity to true at the class level.
    	static bool bValidXml = true;

    	// Add in a new node that is not in the schema.
    	// Since we have already validated, no callbacks fire during the add…
    	XmlNode newNode = xmlDoc.CreateElement("BogusElement");
    	newNode.InnerText = "Totally";
    	// Add the new element.
    	xmlDoc.DocumentElement.AppendChild(newNode);
    	// Now we will do validation of the new stuff we added.
    	xmlDoc.Validate(eventHandler);

    	if (bValidXml == true)
    	{
    	    Console.WriteLine("Successfully validated modified XML");
    	}
    	else
    	{
    	    Console.WriteLine("Modified XML did not validate successfully");
    	}

The `ValidationEventHandler` looks like the one from Recipe 15.4:

    	private static void ValidationEventHandler_15_11(object sender,
    	                            ValidationEventArgs e)
    	{
    	    // We got called so this isn't valid.
    	    bValidXml = false;
    	    Console.WriteLine("Validation Error Message: {0}", e.Message);
    	    Console.WriteLine("Validation Error Severity: {0}", e.Severity);
    	    if (e.Exception != null)
    	    {
    	        Console.WriteLine("Validation Error Line Number: {0}",
    	                    e.Exception.LineNumber);
    	        Console.WriteLine("Validation Error Line Position: {0}",
    	                    e.Exception.LinePosition);
    	        Console.WriteLine("Validation Error Source: {0}",
    	                    e.Exception.Source);
    	        Console.WriteLine("Validation Error Source Schema: {0}",
    	                    e.Exception.SourceSchemaObject);
    	        Console.WriteLine("Validation Error Source Uri: {0}",
    	                    e.Exception.SourceUri);
    	        Console.WriteLine("Validation Error thrown from: {0}",
    	                    e.Exception.TargetSite);
    	        Console.WriteLine("Validation Error callstack: {0}",
    	                    e.Exception.StackTrace);
    	    }
    	}

#### Discussion

There is an override to the `XmlDocument.Validate` method that allows you to pass a specific `XmlNode` to validate. If the `XmlDocument` is large, this override to `Validate` should be used:

    	public void Validate(
    	    ValidationEventHandler validationEventHandler,
    	    XmlNode nodeToValidate
    	);

One other approach to this problem is to instantiate an instance of the `XmlNodeReader` with the `XmlDocument` and then create an `XmlReader` with validation settings as shown in Recipe 15.4. This would allow for continual validation while the reader navigated through the underlying XML.

The output from running the code is listed here:

    	Validation Error Message: The element 'Book' in namespace 'http://tempuri.org/Book.
    	xsd' has invalid child element 'BogusElement'. List of possible elements expected:
    	'Chapter' in namespace 'http://tempuri.org/Book.xsd'.
    	Validation Error Severity: Error
    	Validation Error Line Number: 0
    	Validation Error Line Position: 0
    	Validation Error Source:
    	Validation Error Source Schema:
    	Validation Error Source Uri: file:///C:/PRJ32/Book_2_0/C%23Cookbook2/Code/
    	CSharpRecipes/Book.xml
    	Validation Error thrown from:
    	Validation Error callstack:
    	Modified XML did not validate successfully

Notice that the `BogusElement` element that you added was not part of the schema for the `Book` element so you got a validation error along with the information about where the error occurred. Finally, you got a report that the modified XML did not validate correctly.

#### See Also

See Recipe 15.4 and the "XmlDocument.Validate" topic in the MSDN documentation.

### Recipe 15.12. Extending XSLT Transformations

#### Problem

You want to perform operations that are outside the scope of XSLT to include data in the transformed result.

#### Solution

Add an extension object to the transformation that can perform the operations necessary based on the node it is passed. This can be accomplished by using the `XsltArgumentList.AddExtensionObject` method. This object you've created (`XslExtensionObject`) can then be accessed in the XSLT and a method called on it to return the data you want included in the final transformed result:

    	string xmlFile = @"..\..\publications.xml";
    	string xslt = @"..\..\publications.xsl";

    	//Create the XslTransform and load the stylesheet.
    	// This is not XslCompiledTransform because it gives a different empty node.
    	//Create the XslCompiledTransform and load the stylesheet.
    	XslCompiledTransform transform = new XslCompiledTransform();
    	transform.Load(xslt);

    	// Load the XML.
    	XPathDocument xPathDoc = new XPathDocument(xmlFile);

    	// Make up the args for the stylesheet with the extension object.
    	XsltArgumentList xslArg = new XsltArgumentList();
    	// Create our custom extension object.
    	XSLExtensionObject xslExt = new XSLExtensionObject();
    	xslArg.AddExtensionObject("urn:xslext", xslExt);

    	// Send output to the console and do the transformation.
    	using (XmlWriter writer = XmlWriter.Create(Console.Out))
    	{
    	    transform.Transform(xPathDoc, xslArg, writer);
    	}

Note that when the extension object is added to the `XsltArgumentList`, it supplies a namespace of `urn:xslext`. This namespace is used in the XSLT stylesheet to reference the object. The `XSLExtensionObject` is defined here:

    	// Our extension object to help with functionality
    	public class XslExtensionObject
    	{
    	    public XPathNodeIterator GetErrata(XPathNodeIterator nodeChapter)
    	    {
    	        try
    	        {
    	            // In here we could go do other lookup calls
    	            // (XML, database, web service) to get information to
    	            // add back in to the transformation result.
    	            string errata =
    	                 string.Format("<Errata>{0} has {1} errata</Errata>",
    	                   nodeChapter.Current.Value, nodeChapter.Current.Value.Length);
    	            XmlDocument xDoc = new XmlDocument();
    	            xDoc.LoadXml(errata);
    	            XPathNavigator xPathNav = xDoc.CreateNavigator();
    	            xPathNav.MoveToChild(XPathNodeType.Element);
    	            XPathNodeIterator iter = xPathNav.Select(".");
    	            return iter;
    	        }
    	        catch (Exception e)
    	        {
    	            // Eat the exception, as we were unable to use the extension.
    	            // So just return the original iterator.
    	            return nodeChapter;
    	        }
    	    }
    	}

The `GetErrata` method is called during the execution of the XSLT stylesheet to provide data in `XPathNodeIterator` format to the transformation. The `xmlns:xslext` namespace is declared as `urn:xslext`, which matches the namespace value you passed as an argument to the transformation. In the processing of the `Book` template for each `Chapter`, an `xsl:value-of` is called with the select criteria containing a call to the `xslext:GetErrata` method. The stylesheet makes the call as shown here:

    	<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    	    xmlns:xslext="urn:xslext">
    	    <xsl:template match="/">
    	        <xsl:element name="PublishedWorks">
    	            <xsl:apply-templates/>
    	        </xsl:element>
    	    </xsl:template>
    	    <xsl:template match="Book">
    	        <Book>
    	            <xsl:attribute name ="name">
    	                <xsl:value-of select="@name"/>
    	            </xsl:attribute>
    	            <xsl:for-each select="Chapter">
    	                <Chapter>
    	                    <xsl:value-of select="xslext:GetErrata(/)"/>
    	                </Chapter>
    	            </xsl:for-each>
    	        </Book>
    	    </xsl:template>
    	</xsl:stylesheet>

#### Discussion

The ability to call custom code from inside of an XSLT stylesheet is a very powerful one, but one that should be used cautiously. Adding code like this into stylesheets usually renders them less useful in other environments. If the stylesheet never has to be used to transform XML in another parser, this can be a good way to offload work that is either difficult or impossible to accomplish in regular XSLT syntax.

The sample data used in the Solution is presented here:

    	<?xml version="1.0" encoding="utf-8"?>
    	<Publications>
    	    <Book name="Subclassing and Hooking with Visual Basic">
    	        <Chapter>Introduction</Chapter>
    	        <Chapter>Windows System-Specific Information</Chapter>
    	        <Chapter>The Basics of Subclassing and Hooks</Chapter>
    	        <Chapter>Subclassing and Superclassing</Chapter>
    	        <Chapter>Subclassing the Windows Common Dialog Boxes</Chapter>
    	        <Chapter>ActiveX Controls and Subclassing</Chapter>
    	        <Chapter>Superclassing</Chapter>
    	        <Chapter>Debugging Techniques for Subclassing</Chapter>
    	        <Chapter>WH_CALLWNDPROC</Chapter>
    	        <Chapter>WH_CALLWNDPROCRET</Chapter>
    	        <Chapter>WH_GETMESSAGE</Chapter>
    	        <Chapter>WH_KEYBOARD and WH_KEYBOARD_LL</Chapter>
    	        <Chapter>WH_MOUSE and WH_MOUSE_LL</Chapter>
    	        <Chapter>WH_FOREGROUNDIDLE</Chapter>
    	        <Chapter>WH_MSGFILTER</Chapter>
    	        <Chapter>WH_SYSMSGFILTER</Chapter>
    	        <Chapter>WH_SHELL</Chapter>
    	        <Chapter>WH_CBT</Chapter>
    	        <Chapter>WH_JOURNALRECORD</Chapter>
    	        <Chapter>WH_JOURNALPLAYBACK</Chapter>
    	        <Chapter>WH_DEBUG</Chapter>
    	        <Chapter>Subclassing .NET WinForms</Chapter>
    	        <Chapter>Implementing Hooks in VB.NET</Chapter>
    	    </Book>
    	    <Book name="C# Cookbook">
    	        <Chapter>Numbers</Chapter>
    	        <Chapter>Strings and Characters</Chapter>
    	        <Chapter>Classes And Structures</Chapter>
    	        <Chapter>Enums</Chapter>
    	        <Chapter>Exception Handling</Chapter>
    	        <Chapter>Diagnostics</Chapter>
    	        <Chapter>Delegates and Events</Chapter>
    	        <Chapter>Regular Expressions</Chapter>
    	        <Chapter>Collections</Chapter>
    	        <Chapter>Data Structures and Algorithms</Chapter>
    	        <Chapter>File System IO</Chapter>
    	        <Chapter>Reflection</Chapter>
    	        <Chapter>Networking</Chapter>
    	        <Chapter>Security</Chapter>
    	        <Chapter>Threading</Chapter>
    	        <Chapter>Unsafe Code</Chapter>
    	        <Chapter>XML</Chapter>
    	    </Book>
    	    <Book name="C# Cookbook 2.0">
    	        <Chapter>Numbers and Enumerations</Chapter>
    	        <Chapter>Strings and Characters</Chapter>
    	        <Chapter>Classes And Structures</Chapter>
    	        <Chapter>Generics</Chapter>
    	        <Chapter>Collections</Chapter>
    	        <Chapter>Iterators and Partial Types</Chapter>
    	        <Chapter>Exception Handling</Chapter>
    	        <Chapter>Diagnostics</Chapter>
    	        <Chapter>Delegates, Events, and Anonymous Methods</Chapter>
    	        <Chapter>Regular Expressions</Chapter>
    	        <Chapter>Data Structures and Algorithms</Chapter>
    	        <Chapter>File System IO</Chapter>
    	        <Chapter>Reflection</Chapter>
    	        <Chapter>Web</Chapter>
    	        <Chapter>XML</Chapter>
    	        <Chapter>Networking</Chapter>
    	        <Chapter>Security</Chapter>
    	        <Chapter>Threading and Synchronization</Chapter>
    	        <Chapter>Unsafe Code</Chapter>
    	        <Chapter>Toolbox</Chapter>
    	    </Book>
    	</Publications>

#### See Also

See the "XsltArgumentList Class" topic in the MSDN documentation.

### Recipe 15.13. Getting Your Schema in Bulk from Existing XML Files

#### Problem

You have come on to a new project in which XML was used for data transmission, but the programmers who came before you didn't use an XSD for one reason or another. You need to generate beginning schema files for each of the XML examples.

#### Solution

Use the `XmlSchemaInference` class to infer schema from the XML samples. The `GenerateSchemaForDirectory` function in Example 15-9 enumerates all of the XML files in a given directory and processes each of them using the `XmlSchemaInference.InferSchema` method. Once the schemas have been determined, it rolls over the collection and saves out each schema to an XSD file using a `FileStream`.

##### Example 15-9. Generating an XML Schema

    public static void GenerateSchemaForDirectory(string dir)
    {
        // Make sure the directory exists.
        if (Directory.Exists(dir))
        {
            // Get the files in the directory.
            string[] files = Directory.GetFiles(dir, "*.xml");
            foreach (string file in files)
            {
                // Set up a reader for the file.
                using (XmlReader reader = XmlReader.Create(file))
                {
                    XmlSchemaSet schemaSet = new XmlSchemaSet();
                    XmlSchemaInference schemaInference =
                                    new XmlSchemaInference();

                    // Get the schema.
                    schemaSet = schemaInference.InferSchema(reader);

                    string schemaPath = "";
                    foreach (XmlSchema schema in schemaSet.Schemas())
                    {
                        // Make schema file path.
                        schemaPath = Path.GetDirectoryName(file) + @"\" +
                                        Path.GetFileNameWithoutExtension(file) + ".xsd";
                        using (FileStream fs =
                            new FileStream(schemaPath, FileMode.OpenOrCreate))
                        {
                            schema.Write(fs);
                        }
                    }
                }
            }
        }
    }

The `GenerateSchemaForDirectory` method can be called like this:

    	// Get the directory two levels up from where we are running.
    	DirectoryInfo di = new DirectoryInfo(@"..\..");
    	string dir = di.FullName;
    	// Generate the schema.
    	GenerateSchemaForDirectory(dir);

#### Discussion

Having an XSD for the XML files in an application allows for a number of things:

1. Validation of XML presented to the system

2. Documentation of the semantics of the data

3. Programmatic discovery of the data structure through XML reading methods

Using the `GenerateSchemaForDirectory` method can jump-start the process of developing schema for your XML, but each schema should be reviewed by the team member responsible for producing the XML. This will help to ensure that the rules as stated in the schema are correct and also to make sure that additional items like schema default values and other relationships are added. Any relationships that were not present in the example XML files would be missed by the schema generator.

#### See Also

See the "XmlSchemaInference Class" and "XML Schemas (XSD) Reference" topics in the MSDN documentation.

### Recipe 15.14. Passing Parameters to XSLT Transformations

#### Problem

You need to use XSLT to produce information that has a few data items that could change between transformations and you don't want to have a separate XSLT stylesheet for each variation.

#### Solution

Use the `XsltArgumentList` class to pass arguments to the XSLT transformation. This technique allows the program to generate an object for the stylesheet to access (such as a dynamic string) and use while it transforms the given XML file. The `storeTitle` and `pageDate` arguments are passed in to the transformation in the following example. The `storeTitle` is for the title of the comic store and `pageDate` is the date the report is run for. These are added using the `AddParam` method of the `XsltArgumentList` object instance args.

    	XsltArgumentList args = new XsltArgumentList();
    	args.AddParam("storeTitle", "", "Hero Comics Inventory");
    	args.AddParam("pageDate", "", DateTime.Now.ToString("F"));

    	// Create a resolver with default credentials.
    	XmlUrlResolver resolver = new XmlUrlResolver();
    	resolver.Credentials = System.Net.CredentialCache.DefaultCredentials;

The `XsltSettings` class allows changing the behavior of the transformation. If you use the `XsltSettings.Default` instance, the transformation will be done without allowing scripting or the use of the `document()` XSLT function, as they can be security risks. If the stylesheet is from a trusted source, you can just create an `XsltSettings` object and use it, but it is better to be safe. Further changes to the code could open it up to use with untrusted XSLT stylesheets.

    	XslCompiledTransform transform = new XslCompiledTransform();
    	// Load up the stylesheet.
    	transform.Load(@"..\..\ParameterExample.xslt", XsltSettings.Default, resolver);

    	// Perform the transformation.
    	FileStream fs = null;
    	using (fs = new FileStream(@"..\..\ParameterExample.htm",
    	     FileMode.OpenOrCreate, FileAccess.Write))
    	{
    	    transform.Transform(@"..\..\ParameterExample.xml", args, fs);
    	}

To show the different parameters in action, now you change `storeTitle` and `pageDate` again and run the transformation again:

    	// Now change the parameters and reprocess.
    	args = new XsltArgumentList();
    	args.AddParam("storeTitle", "", "Fabulous Adventures Inventory");
    	args.AddParam("pageDate", "", DateTime.Now.ToString("D"));
    	using (fs = new FileStream(@"..\..\ParameterExample2.htm",
    	    FileMode.OpenOrCreate, FileAccess.Write))
    	{
    	    transform.Transform(@"..\..\ParameterExample.xml", args, fs);
    	}

The ParameterExample.xml file contains the following:

    	<?xml version="1.0" encoding="utf-8" ?>
    	<ParameterExample>
    	    <ComicBook name="The Amazing Spider-Man" edition="1"/>
    	    <ComicBook name="The Uncanny X-Men" edition="2"/>
    	    <ComicBook name="Superman" edition="3"/>
    	    <ComicBook name="Batman" edition="4"/>
    	    <ComicBook name="The Fantastic Four" edition="5"/>
    	</ParameterExample>

The ParameterExample.xslt file contains the following:

    	<?xml version="1.0" encoding="UTF-8" ?>
    	<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    	  <xsl:output method="html" indent="yes" />
    	  <xsl:param name="storeTitle"/>
    	    <xsl:param name="pageDate"/>

    	    <xsl:template match="ParameterExample">
    	    <html>
    	      <head/>
    	      <body>
    	        <h3><xsl:text>Brought to you by </xsl:text>
    	        <xsl:value-of select="$storeTitle"/><br/>
    	        <xsl:text> on </xsl:text>
    	        <xsl:value-of select="$pageDate"/>
    	        <xsl:text> &#xd;&#xa;</xsl:text>
    	        </h3>
    	        <br/>
    	        <table border="2">
    	          <thead>
    	            <tr>
    	              <td>
    	                <b>Heroes</b>
    	              </td>
    	              <td>
    	                <b>Edition</b>
    	              </td>
    	              </tr>
    	          </thead>
    	            <tbody>
    	              <xsl:apply-templates/>
    	            </tbody>
    	        </table>
    	      </body>
    	    </html>
    	</xsl:template>

    	  <xsl:template match="ComicBook">
    	  <tr>
    	    <td>
    	      <xsl:value-of select="@name"/>
    	    </td>
    	    <td>
    	      <xsl:value-of select="@edition"/>
    	    </td>
    	    </tr>
    	  </xsl:template>
    	</xsl:stylesheet>

The output from the first transformation to ParameterExample.htm is shown in Figure 15-2.

##### Figure 15-2. Output from the first set of parameters

Output from the second transformation to ParameterExample2.htm is shown in Figure 15-3.

##### Figure 15-3. Output from the second set of parameters

#### Discussion

The ability to pass information to the XSLT stylesheet allows a much greater degree of flexibility when designing reports or user interfaces via XSLT transformations. This capability can help customize the output based on just about any criteria you can think of, as the data being passed in is totally controlled by your program. Once you get the hang of using parameters with XSLT, a whole new level of customization becomes possible. As an added bonus, it is portable between environments (.NET, Xalan, etc.).

#### See Also

See the "XsltArgumentList Class" and "XsltSettings Class" topics in the MSDN documentation.