Serializing Objects in XML C# Help

Serializing the process of persisting an object to disk, Another part of your application, or even a separate application, can deserialize the object and it will be in the same state it was in prior to serialization. The .NET Framework includes a couple of ways to do this, This section looks at the ·System.Xml.Serialization namespace, which contains classes used to serialize objects into XMLdocuments or streams, This means that an objects public properties and public fields are converted into XMLelements or attributes or both.

The most important class in the System.Xml.Serialization namespace is XmlSerializer to serialize an object, you first need to instantiate an XmlSerializer object, specifying the type of the object to serialize. Then you need to instantiate a stream/writer object to write the file to a stream document. The finaJ step is to call the Serialize () method on the XMLSerializer, passing it the stream/writer object and the object to serialize.

Data that can be seriaJized can be primitive types, fields, arrays, and embedded XMLin the form of XmlElement and XmlAttribute objects, To deserialize an object from an XMLdocument, you reverse the process in the previous example. You create a stream/reader and an XmlSerializer object and then pass the stream/reader to the Deserialize () method. This method returns the deserialized object, although it needs to be cast to correct type.

The XML serializer cannot convert private data, only public data, and it cannot serialize object graphs, However, these should not be serious limitations; by carefully designing your classes, they should be easily avoided,  you do need to be able to serialize public and private data as well as an object graph containing many nested objects, you will want to use the System. Runtime, Serialization .Formatters. Binary namespace.

Some of the other tasks that you can accomplish with System.XmI.Serialization classes are:

  1. Determine if the data should be an attribute or element
  2. Specify the namespace
  3. Change the attributer element name

The links between your object and the XMLdocument are the custom C# attributes that annotate your classes, These attributes are what are used to inform file serializer how to write out the data. The xsd . exe tool, which is included with the .NET Framework, can help create these attributes for you. xsd. exe can do the following:

  1. Generate an XMLschema from an XORschema file
  2. Generate an XMLschema from an XMLfile
  3. Generate DAtaSet classes from an XSDschema file
  4. Generate runtime classes that have the custom attributes for XmISerialization
  5. Generate an XSOfile from classes that you have already developed
  6. Generate an XSO file from classes that you have already developed
  7. Determine which programming language the generated code should be in (C#, Visual Basic .NET, or JScript .NET)
  8. Create schemas from types in compiled assemblies

You should refer to the framework documentation for details of command-line options for xsd . exe, Despite these capabilities, you don’t havt to use xsd. exe to create the classes for serialization, The process is quite simple, The following is a simple application that serializes a class. At the beginning of the example, you have very simple code that creates a new Product object, pd, and fills it with some data:

private void buttonl_Click(object sender, EventArgs eJ
{
Ilnew products object
Product pd  new Product () ;
Ilset some properties’
pd.ProductID = 200;
pd.CategoryID z 100;
pd.Discontinued z false;
pd.ProdUctN&me ·Serialize Objects·;
pd.QuantityPerUnit ·6·;
pd.ReorderLevel 1;
pd.SupplierID  1;
pd.UnitPrice 1000;
pd.UnitslnStock  10;
pd.UnitsOnOrder 0;

The Serialize () method of the XmlSeri-.lizer class actually performs the serialization, and it has nine overloads. One of the parameters required is a stream to write the data to. It can be a Stream, TextWriter, or an XmlWriter parameter. In the example, you.create a TextWriter-based object, The next thing to do is to create the XmISerializer-based object, sr. The Xm~Serializer needs to know type information for the object that it is serializing, so you use the typec)f keyword with the type that is to be serialized. After the sr object is created, you call the Serial he ()method, passing in the tr (stream-based object)and the object that you want serialized,in thiscase pd. Be sure to close the stream when you are finished with it:

//new TextWriter and XmlSerializer
TextWriter tr = new StreamWriter(‘serialprod.xml’);
XmlSerializer sr = new XmlSerializer(typeof(Product));
//serialize object
sr.Serialize(tr, pd);
tr .Close () ;
webBrowserl.Navigate(AppDomain.CurrentDomain.BaseDirectory + ‘serialprod.xml’);

Next isthe Product class,the class to be serialized.The only differences between thisand any other class that you may write are the C# attributesthat have been added. The XmlRootAttribute and XmlElementAttribute classes in the attributesinheritfrom the System.Attribute class.Don’t confuse these attributeswith the attributesin an XML document. A C# attributeissimply some declarative information that can be retrieved at runtime by the CLRand Events, In thiscase, the attributes describe how the object should be serialized:

The XmlRootAttribute () invocation in the attribute above the Products class definition identifies this class as a root element (in the XMLfile produced upon serialization). The attribute containing, XmlElementAt tribute () identifies that the member below the attnbute represents an XML element, You will no notice that the To String () method has been:overridden. This provides the string that the message box will show when you run the deserialize example.

If you take a look at the XML document created during serialization, you will see that it looks like any other XML document that you might have created, which is the point of the exercise:

<?xml version=’l.O’ encoding=’utf-8′?>
<Products xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xmlns:xsd=’http://www.w3.org/2001/XMLS~hema’
Discount=’O’>
<ProductI~>200</ProductID>
<ProductName>Serialize Objects</ProductName>
<SupplierID>l</SupplierID>
<CategoryID>lOO</CategoryID>
<QuantityP~rUnit>6</QuantityPerUnit>
<UnitPrice>1000</UnitPrice>
<UnitsInStock>10</UnitsInStock>
<UnitsOnOrger>O</UnitsOnOrder>
<ReorderLevel>l</ReorderLevel>
<Discontinued>fa’lse</Discontinued>
</Products>

There is nothing out of the ordinary here. You could use this any way that you would use an XML document. You could transform it and display it as HTML, load it into a DataSet using ADO.NET, load an XmlDocument with it, “Or,as you can see in the example, deserialize it and create an object in the same state that pd was in prior to serializing it (which is exactly what you’re doing with the second button).

Next, you add another button event handler to deserialize a new Products-based object, newPad This time you use a FileStream object to read in the XML:

private void button2_Click(object sender, EventArgs e)
(
//~reate a reference to producst type
Pr’oduct newPd;
.;/new filestream to open ser i.elrz ed object
FileStream f = new FileStream(‘serialprod.xml’, FileMode.Open);

Once again, you create a new XmlSerializer, passing in the type information of Product, You can then.make the call to the Deserialize () method. Note that you still need to do an explicit cast when you create the newPad object, At this point, newPad is in exactly the same state that pd was:

//new serializer
XmlSerializer newSr = new XmlSerializer(typeof(Product»;
//deserialize the object
newPd = (Product)newSr.Deserialize(f);
f.Close()
MessageBox.Show(newPad.ToString()

The message box should show you the product ID, product name, and the unit price of the object you just deserialized. This comes from the ToString () override that you implemented in the Product class.

What about situations where you have derived classes and possibly properties that return an array? “XmlSerializer has that covered as well. Here’s a slightly more complex example that deals with these issues.

First, you define three new classes, Product, BookProduct (~rived from PrOduct), and Inventory (which contains both of the other classes). Notice that once ‘gain you have overridden the ToString ( ) method. This time you’re just going to list the items in the Inventory class:
public class BookProduct : Product
{
private string isbnNum;

The Inventory class is the one of interest here. If you are to serialize this class you need to insert an attribute containing xmlArray item constructors for each type that can be added to the array. You should note that XllllArrayI tern is the name of the .NET attribute represented by the XmlArrayItemAt tribu te class.

The first parameter supplied to these constructors is what you would like the element name to be in the XML document that is created during serialization. If you leave off the ElernentName parameter, the elements will be given the same name as the object type (Product and BookProduct.in this case).

The second parameter that must be specified is the type of the object, There is also an xmlArrayAttribute class that you would use if the property were returning an array
of objects or primitive types, Because you are returning different types in the array, you use xmlArrayItemAttribute, which allows the higher level of control.

In the button4_Click () event handler, you create a new Product object and a new Book Product object (newProd and newBook).

You add data to the various properties of each object, and add the objects
to a Product array, You then create a ne Inventory object and pass in the array as a parameter.

You can then serialize the Inventory object to recreate it at a later time:

The XML document looks like this:

The but ton2_Click () event handler implements deserialization of the Inventory object. Note that you iterate through the array in the newly created newInv object to show that it is the same data:
private void button2_Click(object sender, System.EventArgs e)
(
Inventory newInv;
FileStream f=new FileStream(“order.xml”,FileMode.Open);
XmlSerializer newSr=new XmlSerializer(typeof(Inventory));
newInv=(Inventory)newSr.Deserialize(f);
foreach(Product prod in newInv.InventoryItems)
listBoxl.rtems.Add(prod.ProductNa~e);~·
f .Close();

SerIalizatIon WIthout Source Code Access

Well, this all works great, but what if you-dont have access to the source code for the types that are being serialized? You can’t-add the attribute if’you don’t have the source. There is another way, You can use the XmlAttributes class and the Xml AttributeCNerrides class, Together these classes enable you to accomplish exactly what you have Just done, but without adding the attributes. This section looks at an example 01 “ow this works, For this example, imagine that the Inventory, Product, and derived BookProduct classes are in a separate DLL and that you don’t have the source, The Product and BookProduct classes are the same as in the previous example, but you should note that there are now no attributes-added to the Inventory class:

public class Inventory
(
private Product[l stuff;
publi~ Inventory() ()
pu’i-, Product[l InventoryItems
get (return stuff;)
set (stuff=value:.)

Next, you deal ‘ith the serialization in the buttonl_Click () event handler:
private vcid buttonl_Click(object sender, System.EventArgs e)
(
The first step in the serialization process is to create an XmlAttributes object and an
XmlElementAttribute object for each data type that you will be overriding:
XmlAttributes attrs~nc XmlAttributes();
attrs.XmlFlements.Add(new XmlElementAttribute(‘Book’,typeof(BookProduct)));
attrs.Xml ~ments.Add(new XmlElementAttribute(‘Product’,typeof(Product))),
Here you can see that you are adding new XmlElementAttribute objects to the XmlElements collection of the XmlAttribute!> class. The XmlAttribu.tes class has.properties that correspond to the attributes that can be applied; XmlArray and XmlArrayIterns, which you looked at in the previous example, are just a couple of these, You now have an XmlAttributes object with two XmlElementAttribute-based objects added to the XmlElements collection.

The next thing you have todo iscreatean XmlAt tributeOVerrides object:
XmlAttributeOverrides attrOVer=new XmlAttributeOVerrides();
attrOVer.Add(typeof(Inventory),”InventoryItems”,attrs);
The Add () method of thisclasshas two overloads.The firstone takesthe type information of the object to override and the Xml Attributes object that you createdearlier.The other overload,which isthe one you are using,also takes a string value that is the member in the overridden object.In thiscase,you want tooverride the InventoryItems member in the Inventory class, When you createthe XmlSerializer object,you add the XmlAttributeOverrides object as a parameter, Now the XmlSerializer knows which types you want to overrideand what you need to return for those types:

If you execute the Serialize () method, you get this XML output:

<UnitsOnOrder>O</UnitsOnOrder>
<ReorderLevel>O</ReorderLevel>
<Discontinued>false</Discontinued>
<ISBN>123456789</ISBN>
Book>
Inventory>

As you can see, you get the same XML as you did with the earlier example. To deserialize this object and recreate the Inventory-based object that you started out with, you need to create all of the same XmlAttributes, XmlElernentAttribute, and XmlAttributeOVerrides objects that you created when you serialized the object, Once you do that, you can read in the XMLand recreate the Inventory object just as you did before, Here is the code to deserialize the Inventory object:

Note that the first few lines of code are identical to the code you used to serialize the object.

The Sy·tem. Xml. XmlSerialization namespace provides a very powerful tool set for serializing objects to XML. By serializing and deserializing objects to XML instead of to binary format, you are given the option of doing something else with this XML, greatly adding to the flexibility of your designs.

Posted on October 31, 2015 in Manipulating XML

Share the Story

Back to Top