Thinking in C# phần 10 pdf

143 327 0
Thinking in C# phần 10 pdf

Đ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

Chapter 17: XML 789 The Model.Horsepower property is associated with the [XmlIgnore] attribute, which specifies that the XmlSerializer not include the property in the XML document. Deserializing XML The XmlSerializer.Deserialize( ) method converts an XML document into an object of the appropriate type: //:c17:CarFromFile.cs //Compile with: //csc /reference:CarStructure2.exe CarFromFile.cs //Demonstrates XML Deserialization using System; using System.IO; using System.Xml.Serialization; public class CarFromFile { public static void Main(){ XmlSerializer xs = new XmlSerializer(typeof(Car)); FileStream str = new FileStream("car.xml", FileMode.Open); Car c = (Car) xs.Deserialize(str); str.Close(); Console.WriteLine("{0} {1} {2}, {3} {4}", c.Model.Year, c.Model.Maker, c.Model.Make, c.Mileage.Quantity, c.Mileage.Units); } }///:~ This example requires a reference to the Car and other domain objects defined in the CarStructure example, but so long as the public properties of the object suffice to put it into a viable state, the XmlSerializer can both write and read objects to any kind of Stream. Can’t serialize cycles This is a perfectly legitimate object-oriented relationship: Yang Yin 1111 Figure 17-2: Cyclical relationships can be hard to express in XML 790 Thinking in C# www.ThinkingIn.NET In this relationship, a Yang contains a reference to a Yin and a Yin to a Yang. It is possible for both objects to contain references to each other; the data structure may have a cycle. XML does not use references to relate objects, it uses containment. One object contains another, which in turn contains its subobjects. There are no references native to XML. If the XmlSerializer detects a cycle, it throws an InvalidOperationException, as this example demonstrates: //:c17:YinYang.cs //Throws an exception due to cyclical reference using System; using System.Xml.Serialization; public class Yin { Yang yang; public Yang Yang{ get{ return yang;} set { yang = value;} } public Yin(){ } } public class Yang { Yin yin; public Yin Yin{ get{ return yin;} set { yin = value;} } public Yang(){ } public static void Main(){ Yin yin = new Yin(); Yang yang = new Yang(); //Set up cycle yin.Yang = yang; yang.Yin = yin; XmlSerializer xs = new XmlSerializer(typeof(Yin)); //Throws InvalidOperationException Chapter 17: XML 791 xs.Serialize(Console.Out, yin); } }///:~ If you wish to use XML to serialize an object structure that might contain cycles, you will have to create your own proxy for references. This will always require the use of unique text-based ids in lieu of references, the use of [XmlIgnore] and the dynamic “reattachment” of references based on the XML Ids. Throughout this book, we’ve often used the phrase “This is an example of the X design pattern.” Here, we have what seems to be the opposite case, a situation where we see a common problem (XML serialization of cyclical references) and can identify a path towards a general solution. There’s a certain temptation to design something and present it as “the Mock Reference pattern” (or whatever). However, probably the most distinctive feature of the seminal books in the patterns movement (Design Patterns and Pattern-Oriented Software Architecture) is that they were based on software archaeology; patterns were recognized in existing, proven software solutions. There are no .NET patterns yet and very few XML patterns; there simply has not been enough time for a variety of design templates to prove themselves in the field. Having said that, let’s take a crack at a serializable Yin-Yang object structure: //:c17:SerializedYinYang.cs // Can serialize cycles using System; using System.IO; using System.Collections; using System.Xml.Serialization; public class Yin { static Hashtable allYins = new Hashtable(); public static Yin YinForId(Guid g){ return(Yin) allYins[g]; } Yang yang; public Yang Yang{ get{ return yang;} set { yang = value; } 792 Thinking in C# www.MindView.net } Guid guid = Guid.NewGuid(); [XmlAttribute] public Guid Id{ get { return guid;} set{ lock(typeof(Yin)){ allYins[guid] = null; allYins[value] = this; guid = value; } } } public Yin(){ allYins[guid] = this; } } public class Yang { Yin yin; [XmlIgnore] public Yin Yin{ get{ return yin;} set { yin = value;} } public Guid YinId{ get { return yin.Id;} set { yin = Yin.YinForId(value); if (yin == null) { yin = new Yin(); yin.Id = value; } } } Guid guid = Guid.NewGuid(); Chapter 17: XML 793 [XmlAttribute] public Guid Id{ get { return guid;} set { guid = value;} } public Yang(){ } public static void Main(){ Yin yin = new Yin(); Yang yang = new Yang(); yin.Yang = yang; yang.Yin = yin; XmlSerializer xs = new XmlSerializer(typeof(Yin)); MemoryStream memStream = new MemoryStream(); xs.Serialize(memStream, yin); memStream.Position = 0; StreamReader reader = new StreamReader(memStream); string xmlSerial = reader.ReadToEnd(); Console.WriteLine(xmlSerial); Console.WriteLine("Creating new objects"); memStream.Position = 0; Yin newYin = (Yin) xs.Deserialize(memStream); xs.Serialize(Console.Out, newYin); Yang newYang = newYin.Yang; Yin refToNewYin = newYang.Yin; if (refToNewYin == newYin) { Console.WriteLine("\nCycle re-established"); } if (newYin == yin) { Console.WriteLine("Objects are the same"); } else { Console.WriteLine("Objects are different"); } } }///:~ 794 Thinking in C# www.ThinkingIn.NET This program relies on the Guid structure, which is a “globally unique identifier” value; both classes have Id properties associated with the XmlAttributeAttribute that can serve to uniquely identify the objects over time. 2 The Yin class additionally has a static Hashtable allYins that returns the Yin for a particular Guid. The Yin( ) constructor and the Yin.Id.set method update the allYins keys so that allYins and YinForId( ) properly return the Yin for the particular Guid. The Yang class property Yin is marked with [XmlIgnore] so that the XmlSerializer won’t attempt to do a cycle. Instead, Yang.YinId is serialized. When Yang.YinId.set is called, the reference to Yang.Yin is reestablished by calling Yin.YinForId( ). The Yang.Main( ) method creates a Yin and a Yang, establishes their cyclical relationship, and serializes them to a MemoryStream. The MemoryStream is printed to the console, gets its Position reset, and is then passed to XmlSerializer.Deserialize( ), creating new Yin and Yang objects. Although newYin and newYang have the same Id values and the same cyclical relationships that the original yin and yang had, they are new objects, as Figure 17-3 illustrates. 2 It’s theoretically possible for GUIDs generated at different times to have the same value, but it’s exceedingly rare. That’s why GUIDs are only “globally” and not “universally” unique. Chapter 17: XML 795 yang : YangYang.Main yin : Yin allYins xs : XmlSerializer new new [yinGuid] = yin Yang = yang Yin = yin Serialize(yin) Deserialize(memStream) newYang : Yang new Id.set(yinGuid) [yinGuid] = null changes "official" Yin for yinGuid from yin to newYin [yinGuid] = newYin new YinId.set(yinGuid) Get(yinGuid) newYin newYin : Yin newYin 796 Thinking in C# www.MindView.net Figure 17-3: Reconstructing cycles from an XML document The output of the program looks like this, although the Guids will be different: <?xml version="1.0"?> <Yin xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Id="8342aaa3-31e4-4d56-95fc-0959301a7ccf"> <Yang Id="531ba739-673e-4840-a2c1-3027f9e60d9f"> <YinId>8342aaa3-31e4-4d56-95fc-0959301a7ccf</YinId> </Yang> </Yin> Creating new objects <?xml version="1.0" encoding="IBM437"?> <Yin xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Id="8342aaa3-31e4-4d56-95fc-0959301a7ccf"> <Yang Id="531ba739-673e-4840-a2c1-3027f9e60d9f"> <YinId>8342aaa3-31e4-4d56-95fc-0959301a7ccf</YinId> </Yang> </Yin> Cycle re-established Schemas So far, the only restriction that we’ve placed on the XML is that it be well formed. But XML has an additional capability to specify that only elements of certain types and with certain data be added to the document. Such documents not only are well formed, they are valid. XML documents can be validated in two ways, via Document Type Definition (DTD) files and via W3C XML Schema (XSD) files. The XML Schema definition is still quite new, but is significantly more powerful than DTDs. Most significantly, DTDs are not themselves XML documents, so you can’t create, edit, and reason about DTDs with the same tools and code that you use to work with XML documents. An XML Schema, on the other hand, is itself an XML document, so working with XML Schemas can be done with the same classes and methods that you use to work with any XML document. Another advantage of XML Schemas is that they provide for validity checking of the XML data; if the XML Schema specifies that an element must be a positiveInteger, then a variety of tools can validate the data flowing in or out of your program to confirm the element’s values. This XML Schema validates the data of the CarStructure example: Chapter 17: XML 797 <?xml version="1.0" encoding="utf-8"?> <xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="Car" nillable="true" type="Car" /> <xs:complexType name="Car"> <xs:sequence> <xs:element minOccurs="0" maxOccurs="1" name="Model" type="Model" /> <xs:element minOccurs="0" maxOccurs="1" name="Mileage" type="Mileage" /> <xs:element minOccurs="1" maxOccurs="1" name="AirConditioning" nillable="true" type="AirConditioning" /> </xs:sequence> <xs:attribute name="VIN" type="xs:string" /> </xs:complexType> <xs:complexType name="Model"> <xs:sequence> <xs:element minOccurs="1" maxOccurs="1" name="Year" type="xs:int" /> <xs:element minOccurs="0" maxOccurs="1" name="Manufacturer" type="xs:string" /> <xs:element minOccurs="0" maxOccurs="1" name="Make" type="xs:string" /> </xs:sequence> </xs:complexType> <xs:complexType name="Mileage"> <xs:simpleContent> <xs:extension base="xs:int"> <xs:attribute name="Units" type="xs:string" /> </xs:extension> </xs:simpleContent> </xs:complexType> <xs:complexType name="AirConditioning" /> <xs:element name="Model" nillable="true" type="Model" /> <xs:element name="Mileage" nillable="true" type="Mileage" /> <xs:element name="AirConditioning" nillable="true" type="AirConditioning" /> </xs:schema> 798 Thinking in C# www.ThinkingIn.NET We aren’t going to go over the details, because you’ll never have to handcraft an XML Schema until you’re working at a quite advanced level. The .NET Framework SDK comes with a tool (xsd.exe) that can generate an XML Schema from an already compiled .NET assembly. This example was generated with this command line: xsd CarStructure.exe More commonly, you’ll be working in a domain with a standards group that produces the XML Schema as one of its key technical tasks. If given an XML Schema, the xsd tool can generate classes with public properties that conform to the XML Schema’s specification. In practice, this rarely works without modification of the schema; whether the fault lies in the xsd tool or the widespread lack of experience with XML Schema in vertical industries is difficult to say. You can also use the xsd tool to generate a class descended from type DataSet. As discussed in Chapter 10, a DataSet is an in-memory, disconnected representation of relational data. So with the xsd tool, you can automatically bridge the three worlds of objects, XML, and relational data. ADO and XML From the discussion of ADO in Chapter 10, you’ll remember that a DataSet is an in-memory representation of a relational model. An XmlDataDocument is an XML Doocument whose contents are synchronized with a DataSet, changes to the XML data are reflected in the DataSet’s data, changes to the DataSet’s data are reflected in the XmlDataDocument (as always, committing these changes to the database requires a call to IDataAdapter.Update( )). This example quickly revisits the Northwind database and is essentially a rehash of our first ADO.NET program (you’ll need a copy of NWind.mdb in the current directory): //:c17:NwindXML.cs //Demonstrates ADO to XML bridge using System; using System.Xml; using System.Text; using System.Data; using System.Data.OleDb; class NWindXML { [...]... using System; 800 Thinking in C# www.MindView.net using using using using using System.Xml; System.Text; System.IO; System.Data; System.Data.OleDb; class CarDataSet { public static void Main(){ DataSet carDataSet = new DataSet(); carDataSet.ReadXml( "car.xml", XmlReadMode.InferSchema); Stream stdout = Console.OpenStandardOutput(); carDataSet.WriteXmlSchema(stdout); } }///:~ Combined with the BindingContext... its corresponding TreeNode is changed to highlight the selection When run, the program provides an easy-to-use explorer of XPath functionality: Figure 17-6: The XPath Explorer sample in action 814 Thinking in C# www.ThinkingIn.NET Transforming a document In addition to using XPath to select and navigate XML nodes, the NET Framework provides the System.Xml.Xsl namespace4 for transforming XML documents... to shuffle the “deck of cards” created in the previous example This will require investigating the difficulties associating with creating a “perfect” shuffle algorithm Create an XML schema that describes the “deck of cards” from the previous examples Thinking in C# www.ThinkingIn.NET 7 Write a program that transforms the shuffled “deck of cards” XML document into a “deal” of four five-card hands: ... for selecting a 802 Thinking in C# www.ThinkingIn.NET particular node, the XPathNavigator then produces an XPathNodeIterator to actually move about relative to the position selected by the XPathNavigator These relationships are illustrated in Figure 17-4, which illustrates the behavior of this example: //:c17:CarNavigator.cs //Demonstrates XPathNavigator using System; using System.Xml; using System.Xml.XPath;... //Demonstrates XPathNavigator subselection using System; using System.Xml; using System.Xml.XPath; using System.Data; using System.Data.OleDb; 804 Thinking in C# www.MindView.net class NWindNavigator { public static void Main(string[] args){ DataSet ds = EmployeesOrders("Nwind.mdb"); Console.WriteLine( "DS filled with {0} rows", ds.Tables[0].Rows.Count); XmlDataDocument doc = new XmlDataDocument(ds);... 4 The namespace should be called Xslt since it does not support XSL Formatting Objects Chapter 17: XML 815 //Retrieves 2 tables from Northwind, outputs as XML using System; using System.IO; using System.Xml; using System.Xml.XPath; using System.Data; using System.Data.OleDb; class TwoTables { public static void Main(string[] args){ DataSet ds = EmpAndOrdRel("Nwind.mdb"); FileStream outFile = new FileStream("EmpOrd.xml",... Color.Blue; Invalidate(); } } } catch (Exception e) { Console.WriteLine(e); } } internal void ResetFormatting(){ foreach(TreeNode node in Nodes){ ResetNodeFormatting(node); } Invalidate(); } private void ResetNodeFormatting(TreeNode node){ node.BackColor = DEFAULT_BACKCOLOR; node.ForeColor = DEFAULT_FORECOLOR; foreach(TreeNode child in node.Nodes){ ResetNodeFormatting(child); } } internal void LoadFile(string... The XPathNavigator.MoveToXxx( ) methods are 806 Thinking in C# www.ThinkingIn.NET significantly faster than XPathNavigator.Select( ), but not as flexible for complex navigation If you will be using a single XPathNavigator.Select( ) statement repeatedly, you can use XPathNavigator.Compile( ) to get a reference to an XPathExpression, which you can pass in to an overloaded version of XPathNavigator.Select(... one that we have praised in Chapter 14 Unfortunately, XSLT has not “broken out” as a mainstream technology for Web design; it is not supported in the major tools used by Web designers, the percentage of browsers incapable of displaying XSLTbased pages remains at least in the high teens, and the search engines have difficulty indexing XML-based sites Although you can set things up so that the XSLT transformation... type="xs:string" minOccurs="0" /> A DataSet can also directly read an XML stream, either validating it against an existing schema or inferring a schema from the data Here, the car.xml becomes the source of a DataSet and its inferred schema . "official" Yin for yinGuid from yin to newYin [yinGuid] = newYin new YinId.set(yinGuid) Get(yinGuid) newYin newYin : Yin newYin 796 Thinking in C# www.MindView.net Figure 17-3: Reconstructing cycles. <book> <bookinfo> <title> ;Thinking in C#& lt;/title> <author> <personname> <firstname>Larry</firstname> 802 Thinking in C# www.ThinkingIn.NET <surname>O'Brien</surname>. express in XML 790 Thinking in C# www.ThinkingIn.NET In this relationship, a Yang contains a reference to a Yin and a Yin to a Yang. It is possible for both objects to contain references to each

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

Tài liệu cùng người dùng

Tài liệu liên quan