XML Primer

XML Primer

XML is a language for describing data and the structure of data. XML data is contained in a document, which can be a file, a stream, or any other storage medium, real or virtual, that s capable of holding text. A proper XML document begins with the following XML declaration, which identifies the document as an XML document and specifies the version of XML that the document s contents conform to:

<?xml version="1.0"?>

The XML declaration can also include an encoding attribute that identifies the type of characters contained in the document. For example, the following declaration specifies that the document contains characters from the Latin-1 character set used by Windows 95, 98, and ME:

<?xml version="1.0" encoding="ISO-8859-1"?>

The next example identifies the character set as UTF-16, which consists of 16-bit Unicode characters:

<?xml version="1.0" encoding="UTF-16"?>

The encoding attribute is optional if the document consists of UTF-8 or UTF-16 characters because an XML parser can infer the encoding from the document s first five characters: <?xml . Documents that use other encodings should identify the encodings that they use to ensure that an XML parser can read them.

XML declarations are actually specialized forms of XML processing instructions, which contain commands for XML processors. Processing instructions are always enclosed in <? and ?> symbols. Some browsers interpret the following processing instruction to mean that the XML document should be formatted using a style sheet named Guitars.xsl before it s displayed:

<?xml-stylesheet type="text/xsl" href="Guitars.xsl"?>

You ll see this processing instruction used in an XML Web page near the end of this chapter.

The XML declaration is followed by the document s root element, which is usually referred to as the document element. In the following example, the document element is named Guitars:

<?xml version="1.0"?> <Guitars> ... </Guitars>

The document element is not optional; every document must have one. The following XML is legal because Guitar elements are nested within the document element Guitars:

<?xml version="1.0"?> <Guitars> <Guitar> ... </Guitar> <Guitar> ... </Guitar> </Guitars>

The document in the next example, however, is not legal because it lacks a document element:

<?xml version="1.0"?> <Guitar> ... </Guitar> <Guitar> ... </Guitar>

Element names conform to a set of rules prescribed in the XML 1.0 specification, which you can read at http://www.w3.org/TR/REC-xml. The spec essentially says that element names can consist of letters or underscores followed by letters, digits, periods, hyphens, and underscores. Spaces are not permitted in element names.

Elements

Elements are the building blocks of XML documents. Elements can contain data, other elements, or both, and are always delimited by start and end tags. XML has no predefined elements; you define elements as needed to adequately describe the data contained in an XML document. The following document describes a collection of guitars:

<?xml version="1.0"?> <Guitars> <Guitar> <Make>Gibson</Make> <Model>SG</Model> <Year>1977</Year> <Color>Tobacco Sunburst</Color> <Neck>Rosewood</Neck> </Guitar> <Guitar> <Make>Fender</Make> <Model>Stratocaster</Model> <Year></Year> <Color>Black</Color> <Neck>Maple</Neck> </Guitar> </Guitars>

In this example, Guitars is the document element, Guitar elements are children of Guitars, and Make, Model, Year, Color, and Neck are children of Guitar. The Guitar elements contain no data (just other elements), but Make, Model, Year, Color, and Neck contain data. The line

<Year></Year>

signifies an empty element one that contains neither data nor other elements. Empty elements are perfectly legal in XML. An empty Year element can optionally be written this way for conciseness:

<Year/>

Unlike HTML, XML requires that start tags be accompanied by end tags. Therefore, the following XML is never legal:

<Year>1977

Also unlike HTML, XML is case-sensitive. A <Year> tag closed by a </year> tag is not legal because the cases of the Y s do not match.

Because XML permits elements to be nested within elements, the content of an XML document can be viewed as a tree. Figure 13-1 shows the tree that corresponds to the XML document above. The tree clearly diagrams the parent-child relationships among the document s elements and is helpful in visualizing the document s structure.

Figure 13-1

An XML tree.

Attributes

XML allows you to attach additional information to elements by including attributes in the elements start tags. Attributes are name/value pairs. The following Guitar element expresses Year as an attribute rather than as a child element:

<Guitar Year="1977"> <Make>Gibson</Make> <Model>SG</Model> <Color>Tobacco Sunburst</Color> <Neck>Rosewood</Neck> </Guitar>

Attribute values must be enclosed in single or double quotation marks and may include spaces and embedded quotation marks. (An attribute value delimited by single quotation marks can contain double quotation marks and vice versa.) Attribute names are subject to the same restrictions as element names and therefore can t include spaces. The number of attributes an element can be decorated with is not limited.

When defining a document s structure, it s sometimes unclear especially to XML newcomers whether a given item should be defined as an attribute or an element. In general, attributes should be used to define out-of-band data and elements to define data that is integral to the document. In the example above, it probably makes sense to define Year as an element rather than an attribute because Year provides important information about the instrument in question. But consider the following element definition:

<Guitar Image="MySG.jpeg"> <Make>Gibson</Make> <Model>SG</Model> <Year>1977</Year> <Color>Tobacco Sunburst</Color> <Neck>Rosewood</Neck> </Guitar>

The Image attribute contains additional information that an application might use to decorate a table containing information about the guitar with a picture. Because no one other than the software processing this document is likely to care about the image, and since the image is an adjunct to (rather than a part of) the guitar s definition, Image is properly cast as an attribute instead of an element.

CDATA, PCDATA, and Entity References

Textual data contained in an XML element can be expressed as character data (CDATA), parsed character data (PCDATA), or a combination of the two. Data that appears between <![CDATA[ and ]]> tags is CDATA; any other data is PCDATA. The following element contains PCDATA:

<Color>Tobacco Sunburst</Color>

The next element contains CDATA:

<Color><![CDATA[Tobacco Sunburst]]></Color>

And this one contains both:

<Color>Tobacco <![CDATA[Sunburst]]></Color>

XML parsers ignore CDATA but parse PCDATA that is, interpret it as markup language. The practical implication is that you can put anything between <![CDATA[ and ]]> tags and an XML parser won t care. Data not enclosed in <![CDATA[ and ]]> tags, however, must conform to the rules of XML.

Why does XML distinguish between CDATA and PCDATA? Certain characters notably <, >, and & have special meaning in XML and must be enclosed in CDATA sections if they re to be used verbatim. For example, suppose you wanted to define an element named Range whose value is 0 < x < 100 . Because < is a reserved character, you can t define the element this way:

<Range>0 < x < 100</Range>

You can, however, define it this way:

<Range><[CDATA[0 < x < 100]]></Range>

CDATA sections are useful for including mathematical equations, code listings, and even other XML documents in XML documents.

Another way to include <, >, and & characters in an XML document is to replace them with entity references. An entity reference is a string enclosed in & and ; symbols. XML predefines the following entities:

Symbol

Corresponding Entity

<

lt

>

gt

&

amp

apos

quot

Here s an alternative method for defining a Range element with the value 0 < x < 100 :

<Range>0 &lt; x &lt; 100</Range>

You can also represent characters in PCDATA with character references, which are nothing more than numeric character codes enclosed in &# and ; symbols, as in

<Range>0 &#60; x &#60; 100</Range>

Character references are useful for representing characters that can t be typed from the keyboard. Entity references are useful for escaping the occasional special character, but for large amounts of text containing arbitrary content, CDATA sections are far more convenient.

Namespaces

Namespaces, which are documented in the XML namespaces specification at http://www.w3.org/TR/REC-xml-names, are a crucial component of XML. Namespaces are a mechanism for qualifying element and attribute names to avoid naming collisions. The following example defines three namespaces and three namespace prefixes. It uses the namespace prefixes to qualify its elements so that the elements won t clash if used in the same document with other elements having the same names but different definitions:

<?xml version="1.0"?> <win:Guitars xmlns:win="http://www.wintellect.com/classic-guitars" xmlns:gibson="http://www.gibson.com/finishes" xmlns:fender="http://www.fender.com/finishes"> <win:Guitar> <win:Make>Gibson</win:Make> <win:Model>SG</win:Model> <win:Year>1977</win:Year> <gibson:Color>Tobacco Sunburst</gibson:Color> <win:Neck>Rosewood</win:Neck> </win:Guitar> <win:Guitar> <win:Make>Fender</win:Make> <win:Model>Stratocaster</win:Model> <win:Year>1990</win:Year> <fender:Color>Black</fender:Color> <win:Neck>Maple</win:Neck> </win:Guitar> </win:Guitars>

In this example, the namespace names are URLs. You can name a namespace anything you want, but in accordance with the namespaces specification, developers typically use Uniform Resource Identifiers (URIs). The URI doesn t have to point to anything; it just has to be unique. It s not far-fetched to think that two different people somewhere in the world might define an XML element named Guitar, but it s unlikely that both will assign it to a namespace named http://www.wintellect.com/classic-guitars.

A namespace prefix is valid for the element in which the prefix is declared and for the element s children. In the preceding example, the win prefix is both declared and used in the same element:

<win:Guitars xmlns:win="http://www.wintellect.com/classic-guitars" ...>

It s also used to qualify child elements:

<win:Guitar> ... </win:Guitar>

Interestingly enough, an element s attributes are not automatically scoped to a namespace, even if the element is. In the following example, the Image attribute doesn t belong to a namespace:

<win:Guitar Image="MySG.jpeg"> ... </win:Guitar>

However, you can use namespace prefixes to join attributes to namespaces:

<win:Guitar win:Image="MySG.jpeg"> ... </win:Guitar>

Because attributes are scoped by the elements they re associated with, they re typically used without namespace prefixes unless needs dictate otherwise.

XML also supports the concept of default namespaces. A default namespace is declared with an xmlns attribute but without a prefix. The element in which a default namespace is declared and all of its children automatically belong to that namespace unless otherwise specified. The following example is equivalent to the first example in this section, but it uses a default namespace http://www.wintellect.com/classic-guitars to eliminate the repeated win prefixes:

<?xml version="1.0"?> <Guitars xmlns="http://www.wintellect.com/classic-guitars" xmlns:gibson="http://www.gibson.com/finishes" xmlns:fender="http://www.fender.com/finishes"> <Guitar> <Make>Gibson</Make> <Model>SG</Model> <Year>1977</Year> <gibson:Color>Tobacco Sunburst</gibson:Color> <Neck>Rosewood</Neck> </Guitar> <Guitar> <Make>Fender</Make> <Model>Stratocaster</Model> <Year></Year> <fender:Color>Black</fender:Color> <Neck>Maple</Neck> </Guitar> <Guitars>

The Color elements in this example belong to the http://www.gibson.com/finishes and http://www.fender.com/finishes namespaces, but all other elements belong to http://www.wintellect.com/classic-guitars.

Why do document authors use XML namespaces, and when is it appropriate to omit them? XML elements intended for use by one person or application typically have no need for namespaces because their owner can prevent naming collisions. XML elements intended for public consumption, however, should be qualified with namespaces because their owner can t control how they re used or what other elements they re used with.

Document Validity and Schemas

An XML document that conforms to the rules of XML is said to be a well-formed document. The following document is well-formed:

<?xml version="1.0"?> <Guitars> <Guitar> <Make>Gibson</Make> <Model>SG</Model> <Year>1977</Year> <Color>Tobacco Sunburst</Color> <Neck>Rosewood</Neck> </Guitar> <Guitar> <Make>Fender</Make> <Model>Stratocaster</Model> <Year>1990</Year> <Color>Black</Color> <Neck>Maple</Neck> </Guitar> </Guitars>

The document below is not well-formed. In fact, it contains three flaws. Can you spot them?

<?xml version="1.0"?> <Guitar> <Make>Gibson</Make> <Model>SG</Model> <Year>1977</Year> <Color>Tobacco Sunburst</Color> <Neck>Rosewood</Neck> </guitar> <Guitar> <Make>Fender</Make> <Model>Stratocaster</Model> <Year>1990 <Color>Black</Color> <Neck>Maple</Neck> </Guitar>

The first flaw is the lack of a document element. The second is the mismatched case in the first Guitar element s start and end tags. The third is the complete lack of an end tag for the second Year element. Any of these flaws is sufficient to make an XML parser quit and report an error. An easy way to determine whether a document is well-formed is to load it into Internet Explorer. IE will apprise you of any well-formedness errors.

A more stringent test of a document s veracity is whether or not the document is valid. A valid document is one that is well-formed and that conforms to a schema. Schemas define acceptable document structure and content. If you allow other companies to invoice your company by transmitting XML invoices, you can provide them with a schema specifying what format you expect their XML documents to be in. When an invoice arrives, you can validate it against the schema to verify that the sender acceded to your wishes.

In the early days of XML, developers used document type definitions (DTDs) to validate XML document content. Today they use XML Schema Definitions (XSDs), which are described at http://www.w3.org/TR/xmlschema-1 and http://www.w3.org/TR/xmlschema-2. XSD is an XML-based language for describing XML documents and the types that they contain. A full treatment of the language could easily fill a book, but just to give you a feel for what XML schemas are all about, here s one that defines the valid format of an XML document describing guitars:

<?xml version="1.0"?> <xsd:schema  xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:element name="Guitars"> <xsd:complexType> <xsd:choice maxOccurs="unbounded"> <xsd:element name="Guitar"> <xsd:complexType> <xsd:sequence> <xsd:element name="Make" type="xsd:string" /> <xsd:element name="Model" type="xsd:string" /> <xsd:element name="Year" type="xsd:gYear" minOccurs="0" /> <xsd:element name="Color" type="xsd:string" minOccurs="0" /> <xsd:element name="Neck" type="xsd:string" minOccurs="0" /> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:choice> </xsd:complexType> </xsd:element> </xsd:schema>

This schema, which might be stored in an XSD file, can be used to validate the XML document that leads off this section. It says that a valid document must contain a Guitars element, that the Guitars element must contain one or more Guitar elements, and that a Guitar element must contain exactly one Make element and one Model element and may contain one Year element, one Color element, and one Neck element, in that order. It also says that Make, Model, Color, and Neck elements contain string data, while Year contains a Gregorian calendar year. The following XML document is not valid with respect to this schema because its one and only Guitar element lacks a Make element and contains two Color elements:

<?xml version="1.0"?> <Guitars> <Guitar> <Model>SG</Model> <Year>1977</Year> <Color>Tobacco Sunburst</Color> <Color>Gun-Metal Gray</Color> <Neck>Rosewood</Neck> </Guitar> </Guitars>

Schemas are meant to be consumed by computers, not humans. Numerous software tools are available for validating XML documents against schemas and for generating schemas from XML documents. One such tool is the Xsd.exe utility that comes with the .NET Framework SDK. Among other things, Xsd.exe is capable of inferring a schema from an XML document and writing the schema to an XSD file. You may have to tweak the XSD file to make sure the schema reflects your true intent, but changing a few statements here and there in an existing schema is far easier than generating a schema from scratch.

XML Parsers

There s nothing inherently magic about XML itself. What s magic is that software tools called XML parsers are readily available to help you read XML documents and extract data from them while shielding you from the syntax of the language.

Most XML parsers implement one of two popular APIs: DOM or SAX. DOM stands for Document Object Model and is described at http://www.w3.org/TR/DOM-Level-2-Core. SAX stands for Simple API for XML and is an unofficial (that is, non-W3C) standard that grew out of a grass roots effort in the Java community. It s currently documented at http://www.saxproject.org. Both APIs define a programmatic interface that abstracts the physical nature of XML documents, but they differ in how they go about it.

SAX is an event-based API. You provide a SAX parser with one or more interfaces containing known sets of callback methods, and as the parser parses the document, it calls you back to let you know what it found. Consider the following XML document:

<Greeting>Hello, world</Greeting>

An application that wants to read this document using a SAX parser implements a well-known interface containing methods named startDocument, endDocument, startElement, endElement, and characters, among others. As the parser moves through the document, it invokes these methods on the client in the following order:

startDocument // Signals start of document startElement // Signals start of Greeting element characters // Transmits "Hello, world" ... endElement // Signals end of Greeting element endDocument // Signals end of document

Calls to startElement and endElement are accompanied by the element names. The ellipsis following characters indicates that characters is called an indeterminate number of times. Some SAX parsers might call it once and pass Hello, world in one chunk, but others might call it several times and transmit Hello, world in bite-sized pieces. SAX is extremely useful for parsing large documents because it doesn t require entire documents to be read into memory at once. SAX s chief downside is that it s a forward-only, stream-based API; you can t arbitrarily move backward and forward within a document. Nor can you easily identify relationships between items in the document, since a callback from a SAX parser provides precious little information about the context in which the callback occurred.

DOM is an alternative API that reads a document into memory and supports random access to the document s content items. Microsoft provides a free DOM-style parser in a DLL named MSXML.dll, better known as the MSXML parser or simply MSXML. (Newer versions of MSXML support SAX too.) MSXML layers a DOM Level 2 compliant object model onto XML documents. Individual items within a document elements, attributes, comments, text, and so on are represented as nodes. Figure 13-2 shows the in-memory node tree that MSXML builds from the following XML document:

<?xml version="1.0"?> <Guitars> <Guitar Image="MySG.jpeg"> <Make>Gibson</Make> <Model>SG</Model> <Year>1977</Year> <Color>Tobacco Sunburst</Color> <Neck>Rosewood</Neck> </Guitar> </Guitars>

Each block in the diagram represents a node. Rectangles represent element nodes (elements in the XML document), ellipses represent text nodes (textual content within those elements), and the parallelogram represents an attribute. Had the document included processing instructions and other XML items, they too would have been represented as nodes in the tree. Each node is an object that provides methods and properties for navigating the tree and extracting content. For example, each node has a hasChildNodes property that reveals whether the node has child nodes, and firstChild and lastChild properties that return references to child nodes.

Figure 13-2

DOM representation of a simple XML document.

What s it like to use the DOM API to parse an XML document? Check out the example in Figure 13-4. It s an unmanaged console application written in C++ that uses MSXML to load an XML document and parse it for Guitar elements. For each Guitar element that it finds, the application reads the values of the Make and Model subelements and writes them to the console window. If you run it against the XML file in Figure 13-3, the application responds with the following output:

Gibson SG Fender Stratocaster

Guitars.xml

<?xml version="1.0"?> <Guitars> <Guitar Image="MySG.jpeg"> <Make>Gibson</Make> <Model>SG</Model> <Year>1977</Year> <Color>Tobacco Sunburst</Color> <Neck>Rosewood</Neck> </Guitar> <Guitar Image="MyStrat.jpeg" PreviousOwner="Eric Clapton"> <Make>Fender</Make> <Model>Stratocaster</Model> <Year>1990</Year> <Color>Black</Color> <Neck>Maple</Neck> </Guitar> </Guitars>
Figure 13-3

Sample XML document.

How does the ReadXml application work? Briefly, the statement

hr = CoCreateInstance (CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument, (void**) &pDoc);

instantiates a DOMDocument object from a COM class implemented in MSXML.dll and returns an IXMLDOMDocument interface pointer. The statement

hr = pDoc->load (var, &success);

loads an XML document from disk, and the statement

hr = pDoc->getElementsByTagName (tag, &pNodeList);

asks the DOMDocument object for a list of Guitar nodes. The list is returned as an IXMLDOMNodeList interface. The application enumerates the Guitar nodes by calling get_item repeatedly on the node list. Each call to get_item returns an IXMLDOMNode interface pointer representing a node that is in reality a Guitar element. The sample program passes the interface pointer to a local function named ShowGuitarType, which performs various acrobatics involving a node list of its own to find the Make and Model subelements and extract their text. Implemented this way, the application can find Guitar elements anywhere in any XML file and extract their Make and Model elements, no matter what order those elements appear in.

ReadXml.cpp

#include <stdio.h> #include <windows.h> void ShowGuitarType (IXMLDOMNode* pNode); BOOL GetChildElementByTagName (LPOLESTR pName, IXMLDOMNode* pParent, IXMLDOMNode** ppNode); BOOL IsElementNamed (LPOLESTR pName, IXMLDOMNode* pNode); int main (int argc, char* argv[]) { HRESULT hr = CoInitialize (NULL); // Instantiate the MS XML parser IXMLDOMDocument* pDoc; hr = CoCreateInstance (CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument, (void**) &pDoc); if (SUCCEEDED (hr)) { // Load Guitars.xml VARIANT_BOOL success;

 BSTR file = SysAllocString (OLESTR ("Guitars.xml")); VARIANT var; var.vt = VT_BSTR; var.bstrVal = file; pDoc->put_async (VARIANT_FALSE); hr = pDoc->load (var, &success); SysFreeString (file); if (SUCCEEDED (hr) && hr != S_FALSE) { // Get a list of elements named "Guitar" IXMLDOMNodeList* pNodeList; BSTR tag = SysAllocString (OLESTR ("Guitar")); hr = pDoc->getElementsByTagName (tag, &pNodeList); SysFreeString (tag); if (SUCCEEDED (hr)) { // Get a count of the elements returned long count; hr = pNodeList->get_length (&count); if (SUCCEEDED (hr)) { pNodeList->reset (); // Walk the list element by element for (int i=0; i<count; i++) { IXMLDOMNode* pNode; hr = pNodeList->get_item (i, &pNode); if (SUCCEEDED (hr)) { // Show the Make and Model subelements ShowGuitarType (pNode); pNode->Release (); } } } pNodeList->Release (); } } pDoc->Release (); } CoUninitialize (); return 0; } void ShowGuitarType (IXMLDOMNode* pNode) { IXMLDOMNode* pMakeNode; IXMLDOMNode* pModelNode; // Get an IXMLDOMNode pointer to the Make subelement if (GetChildElementByTagName (OLESTR ("Make"), pNode, &pMakeNode)) { // Get the Make subelement's text BSTR make; HRESULT hr = pMakeNode->get_text (&make); if (SUCCEEDED (hr) && hr != S_FALSE) { // Get an IXMLDOMNode pointer to the Model subelement if (GetChildElementByTagName (OLESTR ("Model"), pNode, &pModelNode)) { // Get the Model subelement's text BSTR model; hr = pModelNode->get_text (&model); if (SUCCEEDED (hr) && hr != S_FALSE) { // Output the guitar's make and model wprintf (OLESTR ("%s %s\n"), make, model); SysFreeString (model); } pModelNode->Release (); } SysFreeString (make); } pMakeNode->Release (); } } BOOL GetChildElementByTagName (LPOLESTR pName, IXMLDOMNode* pParent, IXMLDOMNode** ppNode) { // Get a list of nodes that are children of pParent IXMLDOMNodeList* pNodeList; HRESULT hr = pParent->get_childNodes (&pNodeList); if (SUCCEEDED (hr)) { // Get a count of the nodes returned long count; hr = pNodeList->get_length (&count); if (SUCCEEDED (hr)) { pNodeList->reset (); // Walk the list node by node for (int i=0; i<count; i++) { IXMLDOMNode* pNode; hr = pNodeList->get_item (i, &pNode); if (SUCCEEDED (hr)) { // If the node is an element whose name matches // the input name, return an IXMLDOMNode pointer if (IsElementNamed (pName, pNode)) { *ppNode = pNode; pNodeList->Release (); return TRUE; } pNode->Release (); } } } pNodeList->Release (); } return FALSE; } BOOL IsElementNamed (LPOLESTR pName, IXMLDOMNode* pNode) { BOOL retval; // Get the node type DOMNodeType type; HRESULT hr = pNode->get_nodeType (&type); if (SUCCEEDED (hr) && type == NODE_ELEMENT) { // If the node is an element, get its name BSTR name; hr = pNode->get_nodeName (&name); if (SUCCEEDED (hr)) { // If the element name matches the input name, return // TRUE to indicate a match retval = (wcscmp (name, pName) == 0) ? TRUE : FALSE; SysFreeString (name); } } return retval; }

Figure 13-4

XML client written in C++.

Dear reader: the code in Figure 13-4 is hard to write and even harder to maintain. It involves so many COM interface pointers and BSTRs (COM s language-neutral string data type) that the tiniest slip could result in memory leaks. There s too much code like this in the real world, which is one reason why so many applications leak memory and have to be restarted periodically. Admittedly, the code would have been much simpler had it been written in Visual Basic rather than C++, but the fact remains that this is no way to write production code.

Enter the .NET Framework class library. The FCL features a handy little class named XmlDocument that provides a managed DOM implementation and makes parsing truly simple. To demonstrate, Figure 13-5 contains the C# equivalent of the application in Figure 13-4. Which of the two would you rather write and maintain?

ReadXml.cs

using System; using System.Xml; class MyApp { static void Main () { XmlDocument doc = new XmlDocument (); doc.Load ("Guitars.xml"); XmlNodeList nodes = doc.GetElementsByTagName ("Guitar"); foreach (XmlNode node in nodes) { Console.WriteLine ("{0} {1}", node["Make"].InnerText, node["Model"].InnerText); } } }
Figure 13-5

XML client written in C#.



Programming Microsoft  .NET
Applied MicrosoftNET Framework Programming in Microsoft Visual BasicNET
ISBN: B000MUD834
EAN: N/A
Year: 2002
Pages: 101

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net