Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 60 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
60
Dung lượng
1,25 MB
Nội dung
Category category2 = new Category(); category2.CategoryID = 2; category2.CategoryName = “Condiments”; category2.Description = “Sweet and savory sauces, relishes,” + “ spreads, and seasonings”; CategoriesList list = new CategoriesList(); list.AddCategory(category1); list.AddCategory(category2); XmlSerializer serializer = new XmlSerializer(typeof(CategoriesList)); TextWriter writer = new StreamWriter(xmlFilePath); //Serialize the Category and close the TextWriter serializer.Serialize(writer, list); writer.Close(); Response.Write(“File written successfully”); } </script> <html xmlns=”http://www.w3.org/1999/xhtml” > <head runat=”server”> <title> Serializing a Collection Object</title> </head> <body> <form id=”form1” runat=”server”> <div> </div> </form> </body> </html> In this code, two instances of the Category class are created and populated with appropriate values. They are then added to the CategoriesList collection object. After that, the collection object is passed to the constructor of the XmlSerializer object as follows. XmlSerializer serializer = new XmlSerializer(typeof(CategoriesList)); Figure 12-2 shows the resultant XML produced by the page. Deserializing XML The XmlSerializer class provides a Deserialize() method that you can invoke to read an XML stream and use to create and populate objects. You can deserialize from a generic stream, a TextReader, or an XmlReader. The overloads for Deserialize are: Object Deserialize(Stream); Object Deserialize(TextReader) ; Object Deserialize(XmlReader); Object Deserialize(XmlReader, String); Object Deserialize(XmlReader, XmlDeserializationEvents); Object Deserialize(XmlReader, String, XmlDeserializationEvents); The remaining parameters allow you to pass the encoding style and the XmlDeserializationEvents object to the deserialization process. 394 Chapter 12 15_596772 ch12.qxd 12/13/05 11:15 PM Page 394 Figure 12-2 To deserialize a Category object from the file Category.xml (created in the previous example), you can simply open a file stream, instantiate an XmlSerializer, and call Deserialize. The complete code is shown in Listing 12-12. Listing 12-12: Deserializing an XML File into an Object <%@ Page Language=”C#” %> <%@ Import Namespace=”System.IO” %> <%@ Import Namespace=”System.Xml.Serialization” %> <script runat=”server”> void Page_Load(object sender, System.EventArgs e) { string xmlFilePath = @”C:\Data\Category.xml”; XmlSerializer serializer = new XmlSerializer(typeof(Category)); TextReader reader = new StreamReader(xmlFilePath); //Deserialize the Category and close the TextReader Category categoryObj = (Category)serializer.Deserialize(reader); reader.Close(); Response.Write(“CategoryID: “ + categoryObj.CategoryID + “<br>”); Response.Write(“Category Name: “ + categoryObj.CategoryName + “<br>”); Response.Write(“Category Description: “ + categoryObj.Description + “<br>”); } </script> <html xmlns=”http://www.w3.org/1999/xhtml” > <head runat=”server”> <title>Simple XML Deserialization</title> </head> <body> <form id=”form1” runat=”server”> <div> 395 XML Serialization 15_596772 ch12.qxd 12/13/05 11:15 PM Page 395 </div> </form> </body> </html> As with serialization using the Serialize() method, the deserialization also requires that the XmlSerializer object be constructed using the type of the object that is being deserialized. XmlSerializer serializer = new XmlSerializer(typeof(Category)); You then create an instance of the StreamReader object passing in the path to the XML file as an argument. TextReader reader = new StreamReader(xmlFilePath); After that, you invoke the Deserialize() method with the StreamReader object as an argument. Category categoryObj = (Category)serializer.Deserialize(reader); Note that the return value of the Deserialize method is of type Object and it needs to be typecast into Category object. After you have the Category object populated with the values, you can display them onto the browser using the Response.Write statements. Handling Events Raised by the XmlSerializer If the input stream does not match what is expected, the deserialization process will attempt to recover as best it can, but as a result one or more objects might be set to null when the procedure has completed. To help you handle these situations, the XmlSerializer class publishes four events that you can trap. These events are raised when certain conditions arise. Table 12-3 lists the events that the XmlSerializer class triggers during the deserialization process. Table 12-3. Events of the XmlSerializer Class Event Description UnknownAttribute Fires when the XmlSerializer encounters an XML attribute of unknown type during deserialization UnknownElement Fires when the XmlSerializer encounters an XML element of unknown type during deserialization UnknownNode Fires when the XmlSerializer encounters any XML node, including Attribute and Element during deserialization UnreferencedObject Fires when the XmlSerializer encounters a recognized type that is not used during deserialization; occurs during the deserialization of a SOAP-encoded XML stream 396 Chapter 12 15_596772 ch12.qxd 12/13/05 11:15 PM Page 396 You can catch these events by creating an appropriate delegate and referencing a method to be executed when the event is raised. The System.Xml.Serialization namespace supplies a delegate for each of these events: ❑ XmlAttributeEventHandler ❑ XmlElementEventHandler ❑ XmlNodeEventHandler ❑ UnreferencedObjectEventHandler You subscribe to an event by hooking up the delegate for the UnknownElement event with the XmlSerializer object. The following code shows how to set up the event handler for the UnknownElement method: serializer.UnknownElement += new XmlElementEventHandler(XmlSerializer_UnknownElement); After you have an event handler registered, you can declare the event handler. void XmlSerializer_UnknownElement(object sender, XmlElementEventArgs e) { //logic } Listing 12-13 shows how to set up event handlers to log the event details about nodes the XmlSerializer could not map to any class members to the browser. Listing 12-13: Handling Events Raised by the XmlSerializer Class <%@ Page Language=”C#” %> <%@ Import Namespace=”System.IO” %> <%@ Import Namespace=”System.Xml” %> <%@ Import Namespace=”System.Xml.Serialization” %> <script runat=”server”> void Page_Load(object sender, System.EventArgs e) { string xmlFilePath = @”C:\Data\Category.xml”; XmlSerializer serializer = new XmlSerializer(typeof(Category)); serializer.UnknownElement += new The EventArgs parameter passed to the event handler contains information about the unexpected element and the position in the input stream at which it occurred. For example, when the XmlSerializer fires for an unmapped attribute in the XML stream, it passes a reference to itself and an XmlAttributeEventArgs object to the registered event handler. The arguments object contains the line number and posi- tion of the attribute within the deserialized XML document, as well as the attribute itself. You can use this information to take some corrective action or record the fact that some unexpected input was received. 397 XML Serialization 15_596772 ch12.qxd 12/13/05 11:15 PM Page 397 XmlElementEventHandler(XmlSerializer_UnknownElement); TextReader reader = new StreamReader(xmlFilePath); //Deserialize the Category and close the TextReader Category categoryObj = (Category)serializer.Deserialize(reader); reader.Close(); Response.Write(“<b>Result of Deserialization:” + “</b><br>”); Response.Write(“CategoryID: “ + categoryObj.CategoryID + “<br>”); Response.Write(“Category Name: “ + categoryObj.CategoryName + “<br>”); } void XmlSerializer_UnknownElement(object sender, XmlElementEventArgs e) { Response.Write(“<b>Unknown Element:” + “</b><br>”); Response.Write(“Unknown Element Name: “ + e.Element.Name + “<br>”); Response.Write(“Unknown Element Value: “ + e.Element.InnerText + “<br><br>”); } </script> <html xmlns=”http://www.w3.org/1999/xhtml” > <head runat=”server”> <title>Handling Events Raised by XmlSerializer </title> </head> <body> <form id=”form1” runat=”server”> <div> </div> </form> </body> </html> This code assumes the following declaration of the Category class. public class Category { public long CategoryID; public string CategoryName; } As you can see, the Category class is missing the Description field that was used in the previous exam- ples. The contents of the XML file used as an input XML document to the Listing 12-13 are as follows: <?xml version=”1.0” encoding=”utf-8”?> <Category xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=”http://www.w3.org/2001/XMLSchema”> <CategoryID>1</CategoryID> <CategoryName>Beverages</CategoryName> <Description>Soft drinks, coffees, teas, beers, and ales</Description> </Category> By comparing the Category class and the contents of the XML file, you can see that the Description node in the XML file does not have a matching field in the Category class. When you request the code in Listing 12-13 in a browser, you will see the output shown in Figure 12-3. 398 Chapter 12 15_596772 ch12.qxd 12/13/05 11:15 PM Page 398 Figure 12-3 During the deserialization, when the Description node is encountered, the XmlSerializer_ UnknownElement event handler is invoked wherein you display information about the unexpected node in the browser. void XmlSerializer_UnknownElement(object sender, XmlElementEventArgs e) { Response.Write(“<b>Unknown Element:” + “</b><br>”); Response.Write(“Unknown Element Name: “ + e.Element.Name + “<br>”); Response.Write(“Unknown Element Value: “ + e.Element.InnerText + “<br><br>”); } Note that the XmlElementEventArgs object exposes a property named ObjectBeingSerialized that enables you to get reference to the Category object during the deserialization. This can be very useful when you want to execute some logic based on the contents of the object being populated. Mapping SQL Server Data Using Deserialization When you get it right, XML deserialization is almost trivial, but getting it right relies on you having a valid XML data and making sure that the class or classes you’re attempting to deserialize into are com- patible with the contents of that file. This is a common issue when you are trying to map the results of a sql query onto an object using SQL Server’s XML support and XML serialization. As you have seen in the previous chapters, SQL Server can return the results of a query in XML format. The XML document describing the result of a query corresponds to the serialized version of a custom document and, if you can create a class that matches the schema of the XML document, you will be able to deserialize SQL Server’s XML response into an instance of the custom class. This section describes a technique for moving data out of SQL Server and into an instance of a custom class, without using DataSets. The custom object is an object that represents a customer. The advantage of this approach, as compared to a straight ADO.NET approach based on DataSets, is that you don’t have to worry about related tables and accessing related rows in DataTable objects. This approach also drastically reduces the amount of code required to consume data retrieved from a database. 399 XML Serialization 15_596772 ch12.qxd 12/13/05 11:15 PM Page 399 For the purposes of this example, you use the following sql query that queries the Person.Contact table in the AdventureWorks database and returns the results in the form of an XML document. Select ContactID, FirstName, MiddleName, LastName, EMailAddress from Person.Contact as Contacts where ContactID = 2 for xml auto, elements The last clause of the statement instructs SQL Server to return the result of the query in XML format. The result of this query is an XML document with the following structure: <Contacts> <ContactID>2</ContactID> <FirstName>Catherine</FirstName> <MiddleName>R.</MiddleName> <LastName>Abel</LastName> <EMailAddress>catherine0@adventure-works.com</EMailAddress> </Contacts> At this point, you can write a class with public fields that reflect the hierarchy of the XML document returned by the query. For the purposes of this example, you manually create the Contact class as shown in Listing 12-14. Listing 12-14: Contact Class [XmlRoot(“Contacts”)] public class Contact { public string ID; public string FirstName; public string MiddleName; public string LastName; } 400 Chapter 12 Although it’s fairly straightforward to build this class manually, you can use the XSD command line tool to automate the generation of the class. To use this tool, copy the XML document returned by the query, paste it into a new text document, and save the document in a file with a short path with extension XML. You can save it as Contacts.xml in the root path; then open a Command Prompt window and switch to the <DriveName>\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin folder. There you can execute the following statement to extract an XML schema from the document: xsd c:\Contacts.xml The XSD utility will process the XML file and will generate a new file with the doc- ument’s XSD schema. The XSD file will be saved in the current folder. Run again the XSD utility, this time specifying the name of the XSD file and two options: the /classes option (to generate the classes that correspond to the specified schema) and the /language option (to generate C# code): xsd Contacts.xsd /classes /language:cs This command will generate a new file, the Contacts.cs file, which contains a seri- alizable class that has the same structure as the XML document. 15_596772 ch12.qxd 12/13/05 11:15 PM Page 400 If you compare the Contacts class declaration with the XML output returned by the sql query, you will see the following anomalies. ❑ The class name is declared as Contact whereas the XML output contains <Contacts> node as the root node. To properly map the <Contacts> element back to the class, the Contact class is decorated with an XmlRoot attribute that specifies the name to be used for deserialization pur- poses. This ensures proper mapping between the SQL Server data and the Contact class. ❑ There is an element named <ContactID> in the XML output whereas the same element is declared as ID in the Contact class declaration. The deserialization code handles this using the XmlAttributeOverrides class that enables you to override an element name at runtime. This is shown in Listing 12-14. ❑ There is an element named <EMailAddress> in the XML output, and the Customer class does not have a corresponding field to hold that value. Listing 12-14 handles this situation by wiring up an UnknownElement event handler with the XmlSerializer object. Now that you have an understanding of the features to implement, take a look at Listing 12-15. Listing 12-15: Mapping Contacts Data in AdventureWorks Database with the Contact Object <%@ Page Language=”C#” %> <%@ Import Namespace=”System.Collections” %> <%@ Import Namespace=”System.Web.Configuration” %> <%@ Import Namespace=”System.Data.SqlClient” %> <%@ Import Namespace=”System.IO” %> <%@ Import Namespace=”System.Xml” %> <%@ Import Namespace=”System.Xml.Serialization” %> <script runat=”server”> void Page_Load(object sender, System.EventArgs e) { Contact cont; //Rename the ContactID to ID element and add it as an attribute XmlElementAttribute contIDElement = new XmlElementAttribute(); contIDElement.ElementName = “ContactID”; XmlAttributes attributesIdCol = new XmlAttributes(); attributesIdCol.XmlElements.Add(contIDElement); XmlAttributeOverrides attrOverrides = new XmlAttributeOverrides(); attrOverrides.Add(typeof(Contact), “ID”, attributesIdCol); string connString = WebConfigurationManager.ConnectionStrings[“adventureWorks”]. ConnectionString; SqlConnection sqlConn = new SqlConnection(connString); sqlConn.Open(); //Instantiate the SqlCommand object and pass the query to be executed SqlCommand sqlCommand = new SqlCommand(“Select ContactID,” + “FirstName, MiddleName, LastName, EMailAddress from Person.Contact “ + “as Contacts where ContactID = 2 for xml auto, elements”, sqlConn); XmlReader reader = sqlCommand.ExecuteXmlReader(); XmlSerializer serializer = new XmlSerializer(typeof(Contact), attrOverrides); serializer.UnknownElement += new XmlElementEventHandler(XmlSerializer_UnknownElement); 401 XML Serialization 15_596772 ch12.qxd 12/13/05 11:15 PM Page 401 if (serializer.CanDeserialize(reader)) { cont = (Contact)serializer.Deserialize(reader); Response.Write(“<b>Result of Deserialization:” + “</b><br>”); Response.Write(“ID: “ + cont.ID + “<br>”); Response.Write(“First Name: “ + cont.FirstName + “<br>”); Response.Write(“Middle Name: “ + cont.MiddleName + “<br>”); Response.Write(“Last Name: “ + cont.LastName + “<br>”); } else Response.Write(“Cannot serialize data”); } void XmlSerializer_UnknownElement(object sender, XmlElementEventArgs e) { Response.Write(“<b>Unknown Element:” + “</b><br>”); Response.Write(“Unknown Element Name: “ + e.Element.Name + “<br>”); Response.Write(“Unknown Element Value: “ + e.Element.InnerText + “<br><br>”); } </script> <html xmlns=”http://www.w3.org/1999/xhtml” > <head runat=”server”> <title> Mapping Contacts Data in AdventureWorks Database with the Customer Object </title> </head> <body> <form id=”form1” runat=”server”> <div> </div> </form> </body> </html> Before examining the code in details, the output produced by Listing 12-14 is shown in Figure 12-4. Figure 12-4 402 Chapter 12 15_596772 ch12.qxd 12/13/05 11:15 PM Page 402 Listing 12-14 starts by mapping the <ContactID> element in the XML output to the ID field in the Contact class through the XmlAttributeOverrides class. After that, it executes the sql query and retrieves the XML output of the query onto an XmlReader object. This XmlReader object is supplied as an input to the Deserialize() method. Before invoking the Deserialize() method, the code also sets up an event handler to handle the UnknownElement event that will be raised when the serializer encounters an unexpected node in the input XML. Generics and XML Serialization With the release of .NET Framework 2.0, the CLR gets a huge addition in expressive power in that Generic types get added with full support in the runtime. XML serialization has been extended to sup- port generic types for serialization and deserialization. Because of the built-in support for generics, you can take advantage of XML serialization to serialize and deserialize particular specializations of the generics type by using the following code. XmlSerializer serializer = new XmlSerializer (typeof(NameValue<int, string>)); What Are Generics? Generics are used to help make the code in your software components much more reusable. They are a type of data structure that contains code that remains the same; however, the data type of the parameters can change with each use. Additionally, the usage within the data structure adapts to the different data type of the passed vari- ables. In summary, a generic is a code template that can be applied to use the same code repeatedly. Each time the generic is used, it can be customized for different data types without needing to rewrite any of the internal code. Generics also allow you to avoid the messy and intensive conversions from reference types to native types. Additionally, you can create routines that are much more type-safe. A generic is defined using a slightly different notation. The following is the basic code for a generic named Compare that can compare two items of the same type and return the larger or smaller value, depending on which method is called: public class Compare<ItemType, ItemType> { public ItemType ReturnLarger(ItemType data, ItemType data2) { // logic } } This generic could be used with any data type, ranging from basic data types such as integers to complex classes and structures. When you use the generic, you identify what data type you are using with it. For example, to use an integer with the previ- ous Compare generic, you would write code similar to the following: Compare<int, int> compare = new Compare<int, int>; int result = compare.ReturnLarger(3, 5); 403 XML Serialization 15_596772 ch12.qxd 12/13/05 11:15 PM Page 403 [...]... but in some cases it also offers a possible solution Listing 12- 18 demonstrates how to set up the exception handler and how to access the InnerException property 4 08 XML Serialization Listing 12- 18: Handling Exceptions Generated by XmlSerializer void Page_Load(object sender, EventArgs e) { try { string xmlFilePath = @”C:\Data\Collections .xml ; List list = new List(); Category categoryObj... a new tool named XML Serializer Generator (Sgen.exe) that allows you to pregenerate those assemblies and deploy them along with your application The XML Serializer Generator creates an XML serialization assembly for types in a specified assembly that can go a long way in improving the startup performance of an XmlSerializer By default (without using the XML Serializer Generator), an XmlSerializer generates... that with a Web Service Method ❑ How to asynchronously invoke a Web service from an ASP.NET page ❑ How to asynchronously invoke a Web service from IE browser ❑ How to control XML serialization of custom types using IXmlSerializable ❑ How to use Schema Importer extensions Chapter 13 XML Web Ser vice XML Web service in ASP.NET is a new model of exposing application logic The entire NET Framework has been... “Manufacturing”; XmlSerializer serializer = new XmlSerializer (typeof(NameValue)); TextWriter writer = new StreamWriter(_xmlFilePath); //Serialize the NameValue object and close the TextWriter serializer.Serialize(writer, nameVal); writer.Close(); lblResult.Text = “File written successfully”; } void Deserialize(object sender, EventArgs e) { XmlSerializer serializer = new XmlSerializer (typeof(NameValue . Listing 12- 14 is shown in Figure 12- 4. Figure 12- 4 4 02 Chapter 12 15_5967 72 ch 12. qxd 12/ 13 /05 11:15 PM Page 4 02 Listing 12- 14 starts by mapping the <ContactID> element in the XML output. whenever XML transport mechanism is required between two object-oriented systems. 409 XML Serialization 15_5967 72 ch 12. qxd 12/ 13 /05 11:15 PM Page 409 15_5967 72 ch 12. qxd 12/ 13 /05 11:15 PM Page 4 10 XML. solution. 4 08 Chapter 12 15_5967 72 ch 12. qxd 12/ 13 /05 11:15 PM Page 4 08 Listing 12- 18: Handling Exceptions Generated by XmlSerializer void Page_Load(object sender, EventArgs e) { try { string xmlFilePath