1. Trang chủ
  2. » Công Nghệ Thông Tin

Apress-Visual CSharp 2010 Recipes A Problem Solution Approach_2 potx

95 558 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề XML Processing
Trường học University of Technology
Chuyên ngành Computer Science
Thể loại Bài luận
Năm xuất bản 2010
Thành phố Hanoi
Định dạng
Số trang 95
Dung lượng 2,05 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

The recipes in this chapter describe how to do the following: • Read, parse, and manipulate XML data recipes 6-1, 6-2, 6-3, and 6-7 • Search an XML document for specific nodes, either by

Trang 2

■ ■ ■

261

XML Processing

One of the most remarkable aspects of the Microsoft NET Framework is its deep integration with XML

In many NET applications, you won’t even be aware you’re using XML technologies—they’ll just be

used behind the scenes when you serialize a Microsoft ADO.NET DataSet, call a web service, or read

application settings from a Web.config configuration file In other cases, you’ll want to work directly with the System.Xml namespaces to manipulate Extensible Markup Language (XML) data Common XML

tasks don’t just include parsing an XML file, but also include validating it against a schema, applying an Extensible Stylesheet Language (XSL) transform to create a new document or Hypertext Markup

Language (HTML) page, and searching intelligently with XPath

In NET 3.5, Microsoft added LINQ to XML, which integrates XML handling into the LINQ model for

querying data sources You can use the same keywords and syntax to query XML as you would a

collection or a database

The recipes in this chapter describe how to do the following:

• Read, parse, and manipulate XML data (recipes 6-1, 6-2, 6-3, and 6-7)

• Search an XML document for specific nodes, either by name (recipe 6-4), by

namespace (recipe 6-5), or by using XPath (recipe 6-6)

• Validate an XML document with an XML schema (recipe 6-8)

• Serialize an object to XML (recipe 9), create an XML schema for a class (recipe

10), and generate the source code for a class based on an XML schema (recipe

Trang 3

The NET Framework provides several different ways to process XML documents The one you use

depends in part upon your programming task One of the most fully featured classes is XmlDocument,

which provides an in-memory representation of an XML document that conforms to the W3C Document

Object Model (DOM) The XmlDocument class allows you to browse through the nodes in any direction,

insert and remove nodes, and change the structure on the fly For details of the DOM specification, go to

www.w3c.org

Note The XmlDocument class is not scalable for very large XML documents, because it holds the entire XML content in memory at once If you want a more memory-efficient alternative, and you can afford to read and process the XML piece by piece, consider the XmlReader and XmlWriter classes described in recipe 6-7

To use the XmlDocument class, simply create a new instance of the class and call the Load method with

a file name, a Stream, a TextReader, or an XmlReader object It is also possible to read the XML from a simple string with the LoadXML method You can even supply a string with a URL that points to an XML document on the Web using the Load method The XmlDocument instance will be populated with the tree

of elements, or nodes, from the source document The entry point for accessing these nodes is the root

element, which is provided through the XmlDocument.DocumentElement property DocumentElement is an XmlElement object that can contain one or more nested XmlNode objects, which in turn can contain more XmlNode objects, and so on An XmlNode is the basic ingredient of an XML file Common XML nodes

include elements, attributes, comments, and contained text

When dealing with an XmlNode or a class that derives from it (such as XmlElement or XmlAttribute),

you can use the following basic properties:

ChildNodes is an XmlNodeList collection that contains the first level of nested

nodes

Name is the name of the node

NodeType returns a member of the System.Xml.XmlNodeType enumeration that

indicates the type of the node (element, attribute, text, and so on)

Value is the content of the node, if it’s a text or CDATA node

Attributes provides a collection of node objects representing the attributes

applied to the element

InnerText retrieves a string with the concatenated value of the node and all nested

nodes

Trang 4

263

InnerXml retrieves a string with the concatenated XML markup for all nested

nodes

OuterXml retrieves a string with the concatenated XML markup for the current

node and all nested nodes

The Code

The following example walks through every element of an XmlDocument using the ChildNodes property

and a recursive method Each node is displayed in a TreeView control, with descriptive text that either

identifies it or shows its content

// Default the file name to the sample document

private void Recipe06_01_Load(object sender, EventArgs e)

// Load the XML document

XmlDocument doc = new XmlDocument();

Trang 5

// Add a TreeNode node that represents this XmlNode

TreeNode newTreeNode = treeNodes.Add(xmlNode.Name);

// Customize the TreeNode text based on the XmlNode

// type and content

}

// Call this routine recursively for each attribute

// (XmlAttribute is a subclass of XmlNode.)

}

Trang 6

265

// Call this routine recursively for each child node

// Typically, this child node represents a nested element

<productName>Blue China Tea Pot</productName>

<description>A trendy update for tea drinkers.</description>

Trang 7

266

Figure 6-1 The displayed structure of an XML document

6-2 Insert Nodes in an XML Document

How It Works

Inserting a node into the XmlDocument class is a two-step process You must first create the node, and then you insert it at the appropriate location You can then call XmlDocument.Save to persist changes

Trang 8

267

To create a node, you use one of the XmlDocument methods starting with the word Create, depending

on the type of node This ensures that the node will have the same namespace as the rest of the

document (Alternatively, you can supply a namespace as an additional string argument.) Next, you

must find a suitable related node and use one of its insertion methods to add the new node to the tree

// Create a new, empty document

XmlDocument doc = new XmlDocument();

XmlNode docNode = doc.CreateXmlDeclaration("1.0", "UTF-8", null);

doc.AppendChild(docNode);

// Create and insert a new element

XmlNode productsNode = doc.CreateElement("products");

doc.AppendChild(productsNode);

// Create a nested element (with an attribute)

XmlNode productNode = doc.CreateElement("product");

XmlAttribute productAttribute = doc.CreateAttribute("id");

productAttribute.Value = "1001";

productNode.Attributes.Append(productAttribute);

productsNode.AppendChild(productNode);

// Create and add the subelements for this product node

// (with contained text data)

XmlNode nameNode = doc.CreateElement("productName");

Trang 10

269

Solution

Create a helper function that accepts a tag name and content, and can generate the entire element at

once Alternatively, use the XmlDocument.CloneNode method to copy branches of an XmlDocument

How It Works

Inserting a single element into an XmlDocument requires several lines of code You can shorten this code

in several ways One approach is to create a dedicated helper class with higher-level methods for adding

elements and attributes For example, you could create an AddElement method that generates a new

element, inserts it, and adds any contained text—the three operations needed to insert most elements

public static XmlNode AddElement(string tagName,

string textContent, XmlNode parent)

public static XmlNode AddAttribute(string attributeName,

string textContent, XmlNode parent)

Trang 11

// Create the basic document

XmlDocument doc = new XmlDocument();

XmlNode docNode = doc.CreateXmlDeclaration("1.0", "UTF-8", null);

doc.AppendChild(docNode);

XmlNode products = doc.CreateElement("products");

doc.AppendChild(products);

// Add two products

XmlNode product = XmlHelper.AddElement("product", null, products);

entire branch, with all nested nodes

Here is an example that creates a new product node by copying the first node:

// (Add first product node.)

// Create a new element based on an existing product

product = product.CloneNode(true);

Trang 12

The XmlDocument class provides a convenient GetElementsByTagName method that searches an entire

document for nodes that have the indicated element name It returns the results as a collection of

Trang 13

272

// Load the document

XmlDocument doc = new XmlDocument();

doc.Load(@" \ \ProductCatalog.xml");

// Retrieve all prices

XmlNodeList prices = doc.GetElementsByTagName("productPrice");

You can also search portions of an XML document by using the XmlElement.GetElementsByTagName

method It searches all the descendant nodes looking for matches To use this method, first retrieve an

XmlNode that corresponds to an element Then cast this object to an XmlElement The following example demonstrates how to find the price node under the first product element:

// Retrieve a reference to the first product

XmlNode product = doc.GetElementsByTagName("products")[0];

// Find the price under this product

Trang 14

273

Solution

Use the overload of the XmlDocument.GetElementsByTagName method that requires a namespace name as

a string argument Additionally, supply an asterisk (*) for the element name if you want to match all

tags

How It Works

Many XML documents contain nodes from more than one namespace For example, an XML document that represents a scientific article might use a separate type of markup for denoting math equations and vector diagrams, or an XML document with information about a purchase order might aggregate client and order information with a shipping record Similarly, an XML document that represents a business-to-business transaction might include portions from both companies, written in separate markup

languages

A common task in XML programming is to retrieve the elements found in a specific namespace You

can perform this task with the overloaded version of the XmlDocument.GetElementsByTagName method that

requires a namespace name You can use this method to find tags by name or to find all the tags in the

specified namespace if you supply an asterisk for the tag name parameter

The Code

As an example, consider the following compound XML document, which includes order and client

information, in two different namespaces (http://mycompany/OrderML and http://mycompany/ClientML):

Trang 15

274

{

// Load the document

XmlDocument doc = new XmlDocument();

doc.Load(@" \ \Order.xml");

// Retrieve all order tags

XmlNodeList matches = doc.GetElementsByTagName("*",

Trang 16

For example, consider the following XML document, which represents an order for two items This

document includes text and numeric data, nested elements, and attributes, and so is a good way to test simple XPath expressions

Basic XPath syntax uses a pathlike notation For example, the path /Order/Items/Item indicates an

<Item> element that is nested inside an <Items> element, which in turn is nested in a root <Order>

element This is an absolute path The following example uses an XPath absolute path to find the name

of every item in an order:

Trang 17

276

// Load the document

XmlDocument doc = new XmlDocument();

doc.Load(@" \ \orders.xml");

// Retrieve the name of every item

// This could not be accomplished as easily with the

// GetElementsByTagName method, because Name elements are

// used in Item elements and Client elements, and so

// both types would be returned

XmlNodeList nodes = doc.SelectNodes("/Order/Items/Item/Name");

foreach (XmlNode node in nodes)

a more detailed reference, refer to the W3C XPath recommendation, at www.w3.org/TR/xpath

Table 6-1 XPath Expression Syntax

/ Starts an absolute path

that selects from the root node

/Order/Items/Item selects all Item elements that are children

of an Items element, which is itself a child of the root Order

element

// Starts a relative path that

selects nodes anywhere

//Item/Name selects all the Name elements that are children of

an Item element, regardless of where they appear in the

document

Trang 18

| Combines multiple paths /Order/Items/Item/Name|Order/Client/Name selects the Name

nodes used to describe a Client and the Name nodes used to describe an Item

Indicates the current

(default) node

If the current node is an Order, the expression /Items refers

to the related items for that order

Indicates the parent node //Name/ selects any element that is parent to a Name, which

includes the Client and Item elements

[ ] Defines selection criteria

that can test a contained

node or an attribute value

/Order[@id="2004-01-30.195496"] selects the Order

elements with the indicated attribute value

/Order/Items/Item[Price > 50] selects products higher

than $50 in price

/Order/Items/Item[Price > 50 and Name="Laser Printer"]

selects products that match two criteria

starts-with

Retrieves elements based

on what text a contained

element starts with

/Order/Items/Item[starts-with(Name, "C")] finds all Item elements that have a Name element that starts with the letter

count Counts elements You

specify the name of the

child element to count or

an asterisk (*) for all

children

/Order/Items/Item[count(Price) = 1] retrieves Item elements that have exactly one nested Price element

Note XPath expressions and all element and attribute names you use inside them are always case-sensitive,

because XML itself is case-sensitive

Trang 19

278

6-7 Read and Write XML Without Loading an Entire

Document into Memory

Problem

You need to read XML from a stream or write it to a stream However, you want to process the

information one node at a time, rather than loading it all into memory with an XmlDocument

Solution

To write XML, create an XmlWriter that wraps a stream and use Write methods (such as

WriteStartElement and WriteEndElement) To read XML, create an XmlReader that wraps a stream, and call Read to move from node to node

allows for a more flexible model Because your code uses the base classes, it can work seamlessly with any derived class For example, you could switch to a validating reader (as shown in the next recipe) without needing to modify your code

To write XML to any stream, you can use the streamlined XmlWriter It provides Write methods that

write one node at a time These include the following:

WriteStartDocument, which writes the document prologue, and WriteEndDocument,

which closes any open elements at the end of the document

WriteStartElement, which writes an opening tag for the element you specify You

can then add more elements nested inside this element, or you can call

WriteEndElement to write the closing tag

WriteElementString, which writes an entire element, with an opening tag, a

closing tag, and text content

WriteAttributeString, which writes an entire attribute for the nearest open

element, with a name and value

Using these methods usually requires less code than creating an XmlDocument by hand, as

demonstrated in recipes 6-2 and 6-3

To read the XML, you use the Read method of the XmlReader This method advances the reader to the next node and returns true If no more nodes can be found, it returns false You can retrieve

Trang 20

XmlTextReader class can access only one node at a time, and it cannot move backward or jump to an

arbitrary node, which gives much less flexibility than the XmlDocument class

The Code

The following console application writes and reads a simple XML document using the XmlWriter and

XmlReader classes This is the same XML document created in recipes 6-2 and 6-3 using the XmlDocument

// Create the file and writer

FileStream fs = new FileStream("products.xml", FileMode.Create);

// If you want to configure additional details (like indenting,

// encoding, and new line handling), use the overload of the Create

// method that accepts an XmlWriterSettings object instead

Trang 21

fs = new FileStream("products.xml", FileMode.Open);

// If you want to configure additional details (like comments,

// whitespace handling, or validation), use the overload of the Create

// method that accepts an XmlReaderSettings object instead

Often, when using the XmlReader, you are searching for specific nodes rather than processing every

element, as in this example The approach used in this example does not work as well in this situation It forces you to read element tags, text content, and CDATA sections separately, which means you need to explicitly keep track of where you are in the document A better approach is to read the entire node and

text content at once (for simple text-only nodes) by using the ReadElementString method You can also use methods such as ReadToDescendant, ReadToFollowing, and ReadToNextSibling, all of which allow you

to skip some nodes

Trang 22

281

For example, you can use ReadToFollowing("Price"); to skip straight to the next Price element,

without worrying about whitespace, comments, or other elements before it (If a Price element cannot

be found, the XmlReader moves to the end of the document, and the ReadToFollowing method returns

any validation exceptions To find all the errors in a document without catching exceptions, handle the

ValidationEventHandler event on the XmlReaderSettings object given as parameter to XmlReader

How It Works

An XML schema defines the rules that a given type of XML document must follow The schema includes rules that define the following:

• The elements and attributes that can appear in a document

• The data types for elements and attributes

• The structure of a document, including what elements are children of other

elements

• The order and number of child elements that appear in a document

• Whether elements are empty, can include text, or require fixed values

XML schema documents are beyond the scope of this chapter, but you can learn much from a

simple example This recipe uses the product catalog first presented in recipe 6-1

At its most basic level, XML Schema Definition (XSD) defines the elements that can occur in an XML document XSD documents are themselves written in XML, and you use a separate predefined element

(named <element>) in the XSD document to indicate each element that is required in the target

document The type attribute indicates the data type Here is an example for a product name:

<xsd:element name="productName" type="xsd:string" />

And here is an example for the product price:

<xsd:element name="productPrice" type="xsd:decimal" />

The basic schema data types are defined at www.w3.org/TR/xmlschema-2 They map closely to NET data types and include string, int, long, decimal, float, dateTime, boolean, and base64Binary—to name

a few of the most frequently used types

Both productName and productPrice are simple types because they contain only character data

Elements that contain nested elements are called complex types You can nest them together using a

Trang 23

282

<sequence> tag, if order is important, or an <all> tag if it is not Here is how you might model the

<product> element in the product catalog Notice that attributes are always declared after elements, and they are not grouped with a <sequence> or <all> tag because order is never important:

<xsd:complexType name="product">

<xsd:sequence>

<xsd:element name="productName" type="xsd:string"/>

<xsd:element name="productPrice" type="xsd:decimal"/>

<xsd:element name="inStock" type="xsd:boolean"/>

</xsd:sequence>

<xsd:attribute name="id" type="xsd:integer"/>

</xsd:complexType>

By default, a listed element can occur exactly one time in a document You can configure this

behavior by specifying the maxOccurs and minOccurs attributes Here is an example that allows an

unlimited number of products in the catalog:

<xsd:element name="product" type="product" maxOccurs="unbounded" />

Here is the complete schema for the product catalog XML:

<xsd:element name="productName" type="xsd:string"/>

<xsd:element name="description" type="xsd:string"/>

<xsd:element name="productPrice" type="xsd:decimal"/>

<xsd:element name="inStock" type="xsd:boolean"/>

</xsd:sequence>

<xsd:attribute name="id" type="xsd:integer"/>

</xsd:complexType>

<! This is the structure the document must match

It begins with a productCatalog element that nests other elements >

<xsd:element name="productCatalog">

<xsd:complexType>

<xsd:sequence>

<xsd:element name="catalogName" type="xsd:string"/>

<xsd:element name="expiryDate" type="xsd:date"/>

Trang 24

The XmlReader class can enforce these schema rules, providing you explicitly request a validating

reader when you use the XmlReader.Create method (Even if you do not use a validating reader, an

exception will be thrown if the reader discovers XML that is not well formed, such as an illegal character, improperly nested tags, and so on.)

Once you have created your validating reader, the validation occurs automatically as you read

through the document As soon as an error is found, the XmlReader raises a ValidationEventHandler

event with information about the error on the XmlReaderSettings object given at creation time If you

want, you can handle this event and continue processing the document to find more errors If you do

not handle this event, an XmlException will be raised when the first error is encountered, and processing

will be aborted

The Code

The next example shows a utility class that displays all errors in an XML document when the ValidateXml

method is called Errors are displayed in a console window, and a final Boolean variable is returned to

indicate the success or failure of the entire validation operation

// Set to true if at least one error exists

private bool failed;

public bool Failed

// Set the type of validation

XmlReaderSettings settings = new XmlReaderSettings();

settings.ValidationType = ValidationType.Schema;

// Load the schema file

XmlSchemaSet schemas = new XmlSchemaSet();

settings.Schemas = schemas;

// When loading the schema, specify the namespace it validates

// and the location of the file Use null to use

Trang 25

// Create the validating reader

XmlReader validator = XmlReader.Create(xmlFilename, settings); failed = false;

// Display the validation error

Console.WriteLine("Validation error: " + args.Message);

Console.WriteLine();

}

}

}

Here is how you would use the class to validate the product catalog:

public class Recipe06_08

Trang 26

If the document is valid, no messages will appear, and the success variable will be set to true But

consider what happens if you use a document that breaks schema rules, such as the

ProductCatalog_Invalid.xml file shown here:

If you attempt to validate this document, the success variable will be set to false, and the output

will indicate each error:

Validating ProductCatalog_Invalid.xml

Validation error: The 'expiryDate' element has an invalid value according to

its data type [path information truncated]

Validation error: The 'productPrice' element has an invalid value according to

Trang 27

286

its data type [path information truncated]

Validation error: The 'inStock' element has an invalid value according to its

data type [path information truncated]

Validation failed

Finally, if you want to validate an XML document and load it into an in-memory XmlDocument, you need to take a slightly different approach The XmlDocument provides its own Schemas property, along with

a Validate method that checks the entire document in one step When you call Validate, you supply a

delegate that points to your validation event handler

Here is how it works:

XmlDocument doc = new XmlDocument();

doc.Load(@" \ \Product_Catalog.xml");

// Specify the schema information

XmlSchemaSet schemas = new XmlSchemaSet();

Use the System.Xml.Serialization.XmlSerializer class to transfer data from your object to XML, and

vice versa You can also mark up your class code with attributes to customize its XML representation

Trang 28

287

How It Works

The XmlSerializer class allows you to convert objects to XML data, and vice versa This process is used

natively by web services and provides a customizable serialization mechanism that does not require a

single line of custom code The XmlSerializer class is even intelligent enough to correctly create arrays

when it finds nested elements

The only requirements for using XmlSerializer are as follows:

The XmlSerializer serializes only properties and public variables

• The classes you want to serialize must include a default zero-argument

constructor The XmlSerializer uses this constructor when creating the new

object during deserialization

All class properties must be readable and writable This is because XmlSerializer

uses the property get accessor to retrieve information and the property set

accessor to restore the data after deserialization

Note You can also store your objects in an XML-based format using NET serialization and System.Runtime Serialization.Formatters.Soap.SoapFormatter In this case, you simply need to make your class

serializable—you do not need to provide a default constructor or ensure all properties are writable However, this gives you no control over the format of the serialized XML

To use XML serialization, you must first mark up your data objects with attributes that indicate the

desired XML mapping You can find these attributes in the System.Xml.Serialization namespace and

include the following:

XmlRoot specifies the name of the root element of the XML file By default,

XmlSerializer will use the name of the class You can apply this attribute to the

class declaration

XmlElement indicates the element name to use for a property or public variable By

default, XmlSerializer will use the name of the property or public variable

XmlAttribute indicates that a property or public variable should be serialized as an

attribute, not an element, and specifies the attribute name

XmlEnum configures the text that should be used when serializing enumerated

values If you don’t use XmlEnum, the name of the enumerated constant will be

used

XmlIgnore indicates that a property or public variable should not be serialized

Trang 29

288

The Code

For example, consider the product catalog first shown in recipe 6-1 You can represent this XML

document using ProductCatalog and Product objects Here’s the class code that you might use: using System;

public string CatalogName;

// Use the date data type (and ignore the time portion in the

// serialized XML)

[XmlElement(ElementName="expiryDate", DataType="date")]

public DateTime ExpiryDate;

// Configure the name of the tag that holds all products

// and the name of the product tag itself

Trang 30

Notice that these classes use the XML serialization attributes to rename element names (using

Pascal casing in the class member names and camel casing in the XML tag names), indicate data types

that are not obvious, and specify how <product> elements will be nested in the <productCatalog>

Using these custom classes and the XmlSerializer object, you can translate XML into objects, and vice versa The following is the code you would need to create a new ProductCatalog object, serialize the

results to an XML document, deserialize the document back to an object, and then display the XML

// Create the product catalog

ProductCatalog catalog = new ProductCatalog("New Catalog",

DateTime.Now.AddYears(1));

Product[] products = new Product[2];

products[0] = new Product("Product 1", 42.99m);

products[1] = new Product("Product 2", 202.99m);

catalog.Products = products;

// Serialize the order to a file

XmlSerializer serializer = new XmlSerializer(typeof(ProductCatalog));

FileStream fs = new FileStream("ProductCatalog.xml", FileMode.Create);

serializer.Serialize(fs, catalog);

fs.Close();

catalog = null;

// Deserialize the order from the file

fs = new FileStream("ProductCatalog.xml", FileMode.Open);

Trang 31

You need to create an XML schema based on one or more C# classes This will allow you to validate XML

documents before deserializing them with the XmlSerializer

Solution

Use the XML Schema Definition Tool (xsd.exe) command-line utility included with the NET

Framework Specify the name of your assembly as a command-line argument, and add the

/t:[TypeName] parameter to indicate the types you want to convert

How It Works

Recipe 6-9 demonstrated how to use the XmlSerializer to serialize NET objects to XML and deserialize

XML into NET objects But if you want to use XML as a way to interact with other applications, business processes, or non–.NET Framework applications, you’ll need an easy way to validate the XML before you attempt to deserialize it You will also need to define an XML schema document that defines the

structure and data types used in your XML format so that other applications can work with it One quick

solution is to generate an XML schema using the xsd.exe command-line utility

The xsd.exe utility is included with the NET Framework If you have installed Microsoft Visual Studio NET, you will find it in a directory like C:\Program Files\Microsoft Visual Studio

.NET\FrameworkSDK\Bin The xsd.exe utility can generate schema documents from compiled assemblies

You simply need to supply the file name and indicate the class that represents the XML document with

the / t:[TypeName] parameter

Usage

For example, consider the ProductCatalog and Product classes shown in recipe 6-9 You could create the

XML schema for a product catalog with the following command line:

xsd Recipe06-09.exe /t:ProductCatalog

You need to specify only the ProductCatalog class on the command line because this class

represents the actual XML document The generated schema in this example will represent a complete

Trang 32

291

product catalog, with contained product items It will be given the default file name schema0.xsd You

can now use the validation technique shown in recipe 6-8 to test whether the XML document can be

successfully validated with the schema

6-11 Generate a Class from a Schema

Problem

You need to create one or more C# classes based on an XML schema You can then create an XML

document in the appropriate format using these objects and the XmlSerializer

Solution

Use the xsd.exe command-line utility included with the NET Framework Specify the name of your

schema file as a command-line argument, and add the /c parameter to indicate you want to generate

class code

How It Works

Recipe 6-10 introduced the xsd.exe command-line utility, which you can use to generate schemas based

on class definitions The reverse operation—generating C# source code based on an XML schema

document—is also possible This is primarily useful if you want to write a certain format of XML

document but you do not want to manually create the document by writing individual nodes with the

XmlDocument class or the XmlWriter class Instead, by using xsd.exe, you can generate a set of full NET

objects You can then serialize these objects to the required XML representation using the

XmlSerializer, as described in recipe 6-9

To generate source code from a schema, you simply need to supply the file name of the schema

document and add the /c parameter to indicate you want to generate the required classes

Usage

For example, consider the schema shown in recipe 6-8 You can generate C# code for this schema with the following command line:

xsd ProductCatalog.xsd /c

This will generate one file (ProductCatalog.cs) with two classes: Product and ProductCalalog These

classes are similar to the ones created in recipe 6-9, except that the class member names match the XML

document exactly Optionally, you can add the /f parameter If you do, the generated classes will be

composed of public fields If you do not, the generated classes will use public properties instead (which simply wrap private fields)

Trang 33

Use the System.Xml.Xsl.XslCompiledTransform class Load the XSLT stylesheet using the

XslCompiledTransform.Load method, and generate the output document by using the Transform method

and supplying a source document

orders.xml document shown in recipe 6-6 into an HTML document with a table, and then displays the

results To perform this transformation, you’ll need the following XSLT stylesheet:

Order <b><xsl:value-of select="Client/@id"/></b>

for <xsl:value-of select="Client/Name"/></p>

Trang 34

The orders.xslt stylesheet contains two template elements (as children of the root stylesheet

element) The first template matches the root Order element When the XSLT processor finds an Order

element, it outputs the tags necessary to start an HTML table with appropriate column headings and

inserts some data about the client using the value-of command, which outputs the text result of an

XPath expression In this case, the XPath expressions (Client/@id and Client/Name) match the id attribute and the Name element

Next, the apply-templates command branches off and performs processing of any contained Item elements This is required because there might be multiple Item elements Each Item element is matched using the XPath expression Items/Item The root Order node is not specified because Order is the current

node Finally, the initial template writes the tags necessary to end the HTML document

If you execute this transform on the sample orders.xml file shown in recipe 6-6, you will end up with

the following HTML document:

Trang 35

294

To apply an XSLT stylesheet in NET, you use the XslCompiledTransform class The following code

shows a Windows-based application that programmatically applies the transformation and then

displays the transformed file in a window using the WebBrowser control:

XslCompiledTransform transform = new XslCompiledTransform();

// Load the XSTL stylesheet

Trang 36

295

Figure 6-2 The stylesheet output for orders.xml

In this example, the code uses the overloaded version of the Transform method that saves the result

document directly to disk, although you could receive it as a stream and process it inside your

application instead The following code shows an alternate approach that keeps the document content

in memory at all times (with no external results file) The XslCompiledTransform writes the results to an XmlWriter that wraps a StringBuilder The content is then copied from the StringBuilder into the

WebBrowser through the handy WebBrowser.DocumentText property The results are identical

StringBuilder htmlContent = new StringBuilder();

XmlWriter results = XmlWriter.Create(htmlContent);

transform.Transform(@" \ \orders.xml", results);

webBrowser1.DocumentText = htmlContent.ToString();

Trang 37

support a range of data sources, as shown in Table 6-2

Table 6-2 Overloads of the XElement.Load Method

Method Description

Load(Stream) Loads the XML data from a stream

Load(String) Loads the XML from a file, the name of which is obtained from the string

Load(TextReader) Loads the XML from a System.IO.TextReader

Load(XMLReader) Loads the XML from a System.Xml.XmlReader (see recipe 6-7)

You can control some of the load options by using the System.Xml.Linq.LoadOptions enumeration

as an additional argument to the Load method—see the NET Framework documentation for details

Trang 38

// Define the path to the sample file

string filename = @" \ \ProductCatalog.xml";

// Load the XML using the file name

Console.WriteLine("Loading using file name");

XElement root = XElement.Load(filename);

// Write out the XML

Console.WriteLine(root);

Console.WriteLine("Press enter to continue");

Console.ReadLine();

// Load via a stream to the file

Console.WriteLine("Loading using a stream");

FileStream filestream = File.OpenRead(filename);

// Load via a textreader

Console.WriteLine("Loading using a TextReader");

TextReader reader = new StreamReader(filename);

// Load via an xmlreader

Console.WriteLine("Loading using an XmlReader");

XmlReader xmlreader = new XmlTextReader(new StreamReader(filename));

Trang 39

Create a new instance of System.Xml.Linq.XElement for the root element Child nodes and attributes can

be added by passing instances of XElement or XAttribute as constructor arguments or by calling the Add

instance method

How It Works

There are two ways to create an XML tree using LINQ to XML The first is to create an instance of

XElement and pass in instances of XElement for child nodes and XAttribute for attributes of the root node

as constructor arguments A simple example follows:

XElement root = new XElement("myrootnode",

new XAttribute("first_attribute", "first_attribute_value"),

new XAttribute("second_attribute", "second_attribute_value"),

new XElement("first_element", "first_element"),

new XElement("second_element", "second_element",

new XAttribute("nested_attribute", "nested_attribute_value"))

);

This approach looks confusing, but starts to make sense once you start to write code yourself You

can supply any number of constructor arguments, and each XElement you create to represent a child

node will accept constructor arguments itself to represent its own children and attributes The preceding example is equivalent to the following XML:

Trang 40

299

The second approach to create an XML tree with XML to LINQ is to create individual instances of

XElement and XAttribute and add them to their parent node using the XElement.Add instance method

See the example in this recipe for a demonstration

In addition to XElement and XAttribute, the System.Xml.Linq namespace includes classes that

represent other XML types—including XComment, XDeclaration, and XCData The NET Framework

contains a full description of each type available, but of particular interest are XDocument and

XDeclaration—these classes allow you to create the standard XML declaration at the start of your data

The Code

The following example creates one element using the constructor arguments and adds another using the

XElement.Add method The root element is then added to an instance of XDocument along with an

XDeclaration The XDocument is written out to the console via an XMLTextWriter, which ensures that the XML header is included (it is omitted if the XDocument instance is passed to Console.WriteLine)

Ngày đăng: 18/06/2014, 16:20

TỪ KHÓA LIÊN QUAN