XML Serialization

I l @ ve RuBoard

Object serialization has been a major topic in the OO world for a number of years . The ability to persist an object and aggregate objects has never been trivial and often has required the development of specialized code to handle specific cases. The ability to provide a generalized framework for serialization often required objects to take an active role in their persistence.

XML has become an important part of doing business today. With an open standard for B2B communication, the necessity of XML now plays a major role in software development. Because XML defines not only the data but also the metadata, it is well suited for object persistence.

C# XML Serialization Support

C# makes use of attributes to support XML serialization. Attributes exist for defining the root element(s), attributes, and elements within the XML document. There are, of course, some basic requirements to support serialization. Any member of a class or struct that needs to be serialized must be either public or have an assessor property. If a member needs to be deserialized, a corresponding setter property is required. There is also a limitation regarding the serialization of collection classes. Currently, only typed arrays can be serialized and deserialized. This, of course, requires the programmer to provide a property that transforms the collection being used into an array, and from an array into the proper collection.

To define the root element, there is the XmlRootAttribute that can be applied to a class or struct . The XmlRootAttribute constructor takes a string that defines the name of the root element within the XML document. Only the top level element, the root element, requires the XmlRoot attribute. All sub elements only need to define the members that will be persisted .

 [XmlRoot("purchase-order")] public class PurchaseOrder {     //PurchaseOrder members } 

The PurchaseOrder class represents the root element for the XML document. Each member within the PurchaseOrder class in Listing 2.2.6 to be serialized will need to be attributed with the XmlElement attribute or XmlAttribute , depending on how the element itself should be persisted.

Listing 2.2.6 Using XML Attributes for Serialization
 1: //File    :PurchaseOrder.cs   2: //Author  :Richard L. Weeks   3: //Purpose :Demonstrate the basics of XML serialization   4: //   5: //Compilation instructions   6: // csc PurchaseOrder.cs /r:System.dll,System.Xml.dll   7:   8:   9: using System;  10: using System.Xml;  11: using System.Xml.Serialization;  12: using System.Collections;  13: using System.IO;  14:  15:  16: ////////////////////////////  17: //Define the Purchase Order  18: [XmlRoot("purchase-order")]  19: public class PurchaseOrder {  20:  21:   //private data  22:   private ArrayList      m_Items;  23:  24:   public PurchaseOrder( ) {  25:          m_Items = new ArrayList();  26:   }  27:  28:   //Properties  29:   [XmlElement("item")]  30:   public Item[] Items {  31:          get {  32:                  Item[] items = new Item[ m_Items.Count ];  33:                  m_Items.CopyTo( items );  34:                  return items;  35:          }  36:          set {  37:                  if( value == null ) return;  38:                  Item[] items = (Item[])value;  39:                  m_Items.Clear();  40:                  foreach( Item i in items )  41:                         m_Items.Add( i );  42:          }  43:       }  44:  45:    //methods  46:    public void AddItem( Item item ) {  47:           m_Items.Add( item );  48:    }  49:  50:    //indexer  51:    public Item this[string sku] {  52:           get {  53:                   //locate the item by sku  54:                   foreach( Item i in m_Items )  55:                          if( i.sku == sku )  56:                                 return i;  57:                   throw( new Exception("Item not found") );  58:           }  59:    }  60:  61:    public void DisplayItems( ) {  62:           foreach( Item i in m_Items )  63:                  Console.WriteLine( i );  64:    }  65:  66: }  67:  68: /////////////////////  69: //Define an item entity  70: public class Item {  71:  72:   //item data  73:   [XmlAttribute("sku")]   public string  sku;  74:   [XmlAttribute("desc")]  public string  desc;  75:   [XmlAttribute("price")] public double  price;  76:   [XmlAttribute("qty")]   public int     qty;  77:  78:  79:   //Default constructor required for XML serialization  80:   public Item( ) {   }  81:  82:   public Item( string Sku, string Desc, double Price, int Qty ) {  83:          sku = Sku;  84:          desc = Desc;  85:          price = Price;  86:          qty = Qty;  87:  88:   }  89:  90:    public override string ToString( ) {  91:           object[] o = new object[] {  sku, desc, price, qty };  92:           return string.Format("{ 0,-5}  { 1,-10}  ${ 2,5:#,###.00}  { 3,3} ", o);  93:    }  94: }  95:  96:  97:  98:  99: /// 100: ///Test the XML Serialization and Deserialization 101: // 102: public class POExample { 103: 104: 105:   public static void Main( ) { 106: 107:          PurchaseOrder po = new PurchaseOrder( ); 108: 109:          po.AddItem( new Item("123","pencil",.15,100) ); 110:          po.AddItem( new Item("321","copy paper", 7.50, 25) ); 111:          po.AddItem( new Item("111","white out", 1.35, 10) ); 112: 113:          po.DisplayItems( ); 114:          Console.WriteLine("Serialization in progress"); 115:          //Serialize the Current Purchase Order 116:          XmlSerializer s = new XmlSerializer( typeof( PurchaseOrder ) ); 117:          TextWriter w = new StreamWriter("po.xml"); 118:          s.Serialize( w, po ); 119:          w.Close(); 120:          Console.WriteLine("Serialization complete\ n\ n"); 121: 122:          Console.WriteLine("Deserialization in progress"); 123:          //Deserialize to a new PO 124:          PurchaseOrder po2;// = new PurchaseOrder( ); 125:          TextReader r = new StreamReader( "po.xml" ); 126:          po2 = (PurchaseOrder)s.Deserialize( r ); 127:          r.Close( ); 128:          Console.WriteLine("Deserialization complete"); 129:          po2.DisplayItems(); 130:    } 131: 132: } 

Listing 2.2.6 puts the C# XML serialization support to work. Again, the PurchaseOrder class represents the top level element for the XML document. The PurchaseOrder class contains an ArrayList to contain the added items. Because there is currently no support for serializing container classes, it is necessary to provide a property that transforms the ArrayList into an array and vise-versa. Line 27 defines an XmlElement for item. The property is implemented such that the ArrayList is converted to any array of type Item[] . This allows for the XML serialization implemented by the XmlSerializer to persist the items.

To deserialize an object, the class or struct must provide a default constructor. In the case of the Item class, because we've defined a parameter-based constructor, there also needs to be a default constructor without parameters so that the object can be created dynamically at runtime. The override ToString method of the Item class plays no role in the XML serialization. Its existence is only to allow for output to the Console stream.

To serialize the PurchaseOrder , an instance is created and items are added to it. Next is the creation of an instance of the XmlSerializer object. The XmlSerializer supports several constructors that allow for extra XML element information ”the default namespace and the root element name. For this example, only the object type is passed in.

For those of you familiar ith COM serialization, the notion of a stream should not be a foreign concept. A stream allows for a generalized view of persistence storage. This notion allows the stream to be connected to a database, file system, or even a network socket.

When the PurchaseOrder sample is executed, the following XML is the result of the serialization.

 <?xml version="1.0" encoding="utf-8"?> <purchase-order xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http:// graphics/ccc.gif www.w3.org/2001/XMLSchema">   <item sku="123" desc="pencil" price="0.15" qty="100" />   <item sku="321" desc="copy paper" price="7.5" qty="25" />   <item sku="111" desc="white out" price="1.35" qty="10" /> </purchase-order> 

The process of deserialization works in basically the same way. The XmlSerializer makes use of the Reflection API to construct the necessary objects and assign the attributes and elements to those items.

Now for something a bit more interesting, instead of the standard boring serialization of objects, we will create a Finite State Machine from an XML representation. Based on the states and productions that are defined for the machine, we can then enter a token string for processing. Figure 2.2.1 depicts a small FSM that will be represented in code using the XML Serialization support provided by .NET.

Figure 2.2.1. 3 State FSM.

graphics/0202fig01.gif

The XML description for the State Machine in Figure 2.2.1 is as follows :

 <?xml version="1.0"?> <fsm xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd="http://www.w3.org/ graphics/ccc.gif 1999/XMLSchema" state_count="3">   <state name="a" is_start="true" is_final="false" production_count="2">     <production token="x" state="b"/>     <production token="y" state="c"/>   </state>   <state name="b" is_start="false" is_final="false" production_count="2">     <production token="x" state="c"/>     <production token="y" state="b"/>   </state>   <state name="c" is_start="false" is_final="true" production_count="2">     <production token="x" state="c"/>     <production token="y" state="c"/>   </state> </fsm> 

The root element fsm defines the FiniteStateMachine to be deserialized. The fsm contains various states, and each state contains the productions that it supports.

The code to process the State Machine has been kept to a minimum to demonstrate the capability of XML serialization. When the sample code is executed, it will prompt the user for a token string to process. For example, the token string xyxyxxy results in a valid string, where as the token string xyxy does not. Only token strings that terminate within the final state are considered valid. Listing 2.2.7 implements the FSM depicted in Figure 2.2.1.

Listing 2.2.7 Implementing an FSM with XML Serialization
 1: /////////////////////////////////////////////////   2: //File    :part02_38.cs   3: //Author  :Richard L. Weeks   4: //Purpose :Make use of XML Serialization to implement   5: //         a finite state machine   6: //   7: //   8: //   9:  10: using System;  11: using System.Xml;  12: using System.Xml.Serialization;  13: using System.IO;  14:  15:  16: /////////////////////////////  17: //The FSM class  18: [XmlRoot("fsm")]  19: public class FSM {  20:  21:       //data members  22:       private int m_StateCount;  23:  24:       [XmlAttribute("state_count")]  25:       public int StateCount {  26:           get {  return m_StateCount; }  27:           set {  28:         m_StateCount = value;  29:         States = new State[m_StateCount];  30:           }  31:       }  32:  33:       [XmlElement("state")]  34:       public State[] States = null;  35:  36:  37:       public bool ProcessString( string s ) {  38:  39:         State CurrentState = GetStartState( );  40:         Console.WriteLine("Start state is { 0} ",CurrentState.Name);  41:         //Process the token string  42:         for( int i = 0; i < s.Length; i++ ) {  43:            string next_state = CurrentState.ProcessToken( string.Format("{ 0} ", graphics/ccc.gif s[i]) );  44:            if( next_state != "" )  45:             CurrentState = GetState( next_state );  46:            else {  47:                         Console.WriteLine( "No production from { 0}  with token { 1} graphics/ccc.gif ", CurrentState.Name, s[i]);  48:                     return false;  49:                    }  50:                Console.WriteLine( "Current State => { 0} ", CurrentState.Name );  51:                 }  52:  53:         return CurrentState.IsFinal;  54:       }  55:  56:       private State GetState( string state_name ) {  57:         //Locate the state name  58:         for( int i = 0; i < States.Length; i++ )  59:             if( States[i].Name == state_name )  60:                 return States[i];  61:         return null;  62:       }  63:  64:       private State GetStartState( ) {  65:         for( int i = 0; i < States.Length; i++ )  66:             if( States[i].IsStart )  67:                 return States[i];  68:         return null;  69:       }  70: }  71:  72: /////////////////////////////  73: //State class  74: [XmlRoot("state")]  75: public class State {  76:  77:     private string         m_Name;  78:     private bool           m_IsStart;  79:     private bool           m_IsFinal;  80:     private int        m_ProductionCount;  81:  82:  83:     [XmlAttribute("name")]  84:     public string Name {  85:         get {  return m_Name; }  86:         set {  m_Name = value; }  87:     }  88:  89:     [XmlAttribute("is_start")]  90:     public bool IsStart {  91:         get {  return m_IsStart; }  92:         set {  m_IsStart = value; }  93:     }  94:  95:     [XmlAttribute("is_final")]  96:     public bool IsFinal {  97:         get {  return m_IsFinal; }  98:         set {  m_IsFinal = value; }  99:     } 100: 101:     [XmlAttribute("production_count")] 102:     public int ProductionCount { 103:         get {  return m_ProductionCount; } 104:         set { 105:             Productions = new Production[value]; 106:             m_ProductionCount = value; 107:             } 108:     } 109: 110:     [XmlElement("production")] 111:     public Production[] Productions = null; 112: 113: 114:     public string ProcessToken( string token ) { 115:         //loop through the productions and return the name of the next state 116:         for( int i = 0; i < Productions.Length; i++ ) { 117:             Console.WriteLine("State { 0}  is evaluating token { 1} ", m_Name, token graphics/ccc.gif ); 118:             Console.WriteLine("Testing Production { 0}  : { 1} ", Productions[i].token, Productions[i].state); graphics/ccc.gif 119:             if( Productions[i].token == token ) 120:                 return Productions[i].state; 121:                 } 122:         return ""; 123:     } 124: 125: } 126: 127: //////////////////////////// 128: //Production struct 129: [XmlRoot("production")] 130: public struct Production { 131: 132:     [XmlAttribute("token")] 133:     public string token; 134: 135:     [XmlAttribute("state")] 136:     public string state; 137: } 138: 139: 140: 141: 142: public class FiniteStateMachine { 143: 144:     public static void Main( ) { 145: 146:         //Deserialize the FSM from the xml file 147:         XmlSerializer s = new XmlSerializer( typeof( FSM ) ); 148:         TextReader tr = new StreamReader( "fsm.xml" ); 149:         FSM fsm = (FSM)s.Deserialize( tr ); 150:         tr.Close( ); 151: 152:         //Get the token string to process 153:         Console.Write("Enter token string to process: "); 154:         string tokens = Console.ReadLine( ); 155:         string result = fsm.ProcessString( tokens ) ? "valid" : "invalid"; 156: 157:         Console.WriteLine("The token string { 0}  is { 1} ", tokens, result ); 158: 159:     } 160: } 
I l @ ve RuBoard


C# and the .NET Framework. The C++ Perspective
C# and the .NET Framework
ISBN: 067232153X
EAN: 2147483647
Year: 2001
Pages: 204

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