8.

XML Serialization

 

Before we get down to expounding the intricate details about XML Serialization, we shall grapple with the basics by creating a file named b.cs, as follows:

 

b.cs

public class yyy

{

public int a1;

public string a2;

}

 

This simple program has a class yyy with two fields, i.e. an int named a1 and a string named a2. Using the csc command, the file is compiled into a dll, as follows:

 

>csc /t:library b.cs

 

The file b.dll now contains the class yyy and its two fields. Now, create a file b1.xml that bears a resemblance to the following:

 

b1.xml

<?xml version="1.0"?>

<yyy>

<a1>10</a1>

<a2>hell</a2>

</yyy>

 

The xml file matches class yyy; and thus, it has the root tag  yyy with two child elements a1 and a2, which represent the variables a1 and a2, respectively. Furthermore, their data types in the xml file are integer and string values, respectively. The following C# program named a.cs is employed to read this xml file:

 

a.cs

using System.Xml.Serialization;

using System.IO;

public class zzz

{

public static void Main()

{

XmlSerializer s = new XmlSerializer(typeof(yyy));

TextReader r = new StreamReader("b1.xml");

yyy a  = (yyy)s.Deserialize(r);

System.Console.WriteLine(a.a1 + " " + a.a2);

r.Close();

TextWriter w = new StreamWriter("b2.xml");

yyy b = new yyy();

b.a1 = 100;

b.a2 = "hi";

s.Serialize(w, b);

w.Close();

}

}

 

>csc a.cs /r:b.dll

 

Output

10 hell

 

The class yyy, which is present in b.dll, is referenced in the program. Hence, the /r option is used while compiling the file a.cs. The program sets off by creating an instance of an XmlSerializer object. This is the class that deserializes objects from dll into XML files. This class determines the manner in which the objects are converted into XML. We shall impart information on this class in just a short while.

 

The constructor is provided with a type object. In all, six overloaded constructors can be used during the creation of an XmlSerializer class, out of which, this constructor happens to be the most elementary and uncomplicated.

 

The XmlSerializer class reads and writes xml files. The parameter that is passed is the type of object that the XmlSerializer has to work with, which in our case refers to the objects of type yyy stored in b.dll. It is crucial to take into account the fact that the xsd program creates the dll file.

 

The yyy class could have been present in the same file a.cs, but we preferred to place it within another file. The StreamReader class reads a file as a stream. The file being read is b1.xml.

 

The XmlSerializer class has a function called Deserialize, which reads an xml file and creates an object. In this case, the object is of type yyy. The Deserialize function returns a data type with the actual return value as the object. There are three overloads available for this function, where the parameter can either be a TextReader, or a Stream or an XmlReader.

 

The object 'a' contains an actual instance of a yyy object, which has been initialized from the file b.xml. Thus, the fields a1 and a2 have been initialized to the values of 10 and 'hell' from the file b1.xml, respectively.

 

The Deserialize function merely reads from an xml file and creates an object of type, which is then passed to the constructor. It also initializes the various fields of the yyy object with the contents of the xml file, as has been delineated here.

 

Then, using the StreamWriter class, a file called b2.xml is created on disk. In order to write a yyy object to the disk, an object called b is used, notwithstanding the fact that the object 'a' could have been reused for this purpose.

 

Two members named a1 and a2 are initialized to 100 and 'Hi', respectively. Then, using the Serialize function that takes the two parameters of a Writer object and the object, the contents are written to the disk. The second parameter is the freshly created yyy object 'b', which is written to the disk as the file b2.xml.

 

b2.xml

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<a1>100</a1>

<a2>hi</a2>

</yyy>

 

The file b2.xml contains the usual directive and the starting yyy root tag. This yyy tag represents the name of the class. At this stage, no entities from the xsi or the xsd namespaces are resorted to.

 

Thus, the prefixes that are created with the help of the attribute xmlns, are of little or no purpose at all. Finally, the two elements a1 and a2 contain the data present in the object 'b'.

 

a.cs

using System.Xml.Serialization;

using System.IO;

public class yyy

{

public int a1;

public string a2;

}

public class zzz

{

public static void Main()

{

XmlSerializer s = new XmlSerializer(typeof(yyy));

TextWriter w = new StreamWriter("b1.xml");

yyy b = new yyy();

b.a1 = 100;

b.a2 = "hi";

s.Serialize(w, b);

w.Close();

TextReader r = new StreamReader("b1.xml");

yyy a  = (yyy)s.Deserialize(r);

System.Console.WriteLine(a.a1 + " " + a.a2);

r.Close();

}

}

 

Output

100 hi

 

b1.xml

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<a1>100</a1>

<a2>hi</a2>

</yyy>

 

Upto this point, we created a class in C# first, and then, used the xsd program to create the dll. Henceforth, we shall use the above program, which conducts itself in a manner similar to the previous one, barring the fact that the class yyy is present in the same file a.cs.

 

At times, it is undeniably easier to deal with a single file. Thus, the object is serialized first, i.e. the file b1.xml is written to, and then, the object is recreated using the same file.

 

XML Serialization involves writing an object created in the file to the disk as an Xml file, whereas, XML De-Serialization entails reading an xml file from disk and initializing an object to the values present in the xml file.

 

a.cs

using System.Xml.Serialization;

using System.IO;

public class yyy

{

[XmlAttributeAttribute()]

public int a1;

public string a2;

}

public class zzz

{

public static void Main()

{

XmlSerializer s = new XmlSerializer(typeof(yyy));

TextWriter w = new StreamWriter("b1.xml");

yyy b = new yyy();

b.a1 = 100;

b.a2 = "hi";

s.Serialize(w, b);

w.Close();

TextReader r = new StreamReader("b1.xml");

yyy a  = (yyy)s.Deserialize(r);

System.Console.WriteLine(a.a1 + " " + a.a2);

r.Close();

}

}

 

Output

100 hi

 

b1.xml

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" a1="100">

<a2>hi</a2>

</yyy>

 

The above program has been slightly modified, wherein the attribute XmlAttributeAttribute is added to the int variable a1. As before, the yyy object is associated with the XmlSerializer using the constructor. A StreamWriter object is also created, which in turn creates the empty file b1.xml on the disk.

 

A yyy object b is created, and the variables a1 and a2 are set to the values 100 and 'hi', respectively. When the object b is serialized, both a1 and a2 become child elements of the yyy root tag by default, since they are variables. The issue of whether it is an element that is more appropriate or an attribute, is highly debatable.

 

The task of the attribute XmlAttributeAttribute is to change this default and to make a1 as an attribute to the tag yyy in the xml file. The end result is that there exists only a single child element in a2 in the xml file.

 

When the object is deserialized, the values get stored in the variables a1 and a2. From the C# point of view, whether the variable is stored as an attribute or as an element, is of no consequence at all. Thus, we can exercise total control on how the class should be represented in an xml file on the disk.

 

a.cs

using System.Xml.Serialization;

using System.IO;

public class yyy

{

public string [] a1;

}

public class zzz

{

public static void Main()

{

XmlSerializer s = new XmlSerializer(typeof(yyy));

TextWriter w = new StreamWriter("b1.xml");

yyy b = new yyy();

string [] ss = {"hi","bye","no" };

b.a1 = ss;

s.Serialize(w, b);

w.Close();

TextReader r = new StreamReader("b1.xml");

yyy a  = (yyy)s.Deserialize(r);

foreach( string s1 in a.a1)

System.Console.Write(s1 + " " );

r.Close();

}

}

Output

hi bye no

 

b1.xml

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<a1>

<string>hi</string>

<string>bye</string>

<string>no</string>

</a1>

</yyy>

 

The above program saves an array to the disk. The class yyy contains an array a1 of type strings. Simultaneously, an array of strings called ss is created and initialized to the three strings 'hi', 'bye' and 'no'. This array is then used to initialize the member a1 of the class yyy. Finally, using the Serialize function, this object b is written to disk, as was done before.

 

In the file b1.xml, the child element a1, which is the name of our array, contains three members enclosed within the data type of string. Resorting to this methodology, the array is saved to the disk.  While reading back the xml file into the object a, the array is automatically initialized to the three members, viz. 'hi', 'bye' and 'no'.

 

a.cs

using System.Xml.Serialization;

using System.IO;

public class xxx

{

public int x1;

public string x2;

public xxx( int a, string b)

{

x1 = a;

x2 = b;

}

public xxx()

{

}

}

public class yyy

{

[XmlArrayAttribute("vijay")]

public xxx [] a1;

public int a2;

}

public class zzz

{

public static void Main()

{

XmlSerializer s = new XmlSerializer(typeof(yyy));

TextWriter w = new StreamWriter("b1.xml");

yyy b = new yyy();

xxx c = new xxx(1,"hi");

xxx d = new xxx(2,"bye");

xxx e = new xxx(3,"no");

xxx [] f = { c,d,e };

b.a1 = f;

b.a2 = 10;

s.Serialize(w, b);

w.Close();

TextReader r = new StreamReader("b1.xml");

yyy a  = (yyy)s.Deserialize(r);

foreach( xxx s1 in a.a1)

System.Console.WriteLine(s1.x1 + " " + s1.x2);

r.Close();

}

}

 

Output

1 hi

2 bye

3 no

 

b1.xml

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<vijay>

<xxx>

<x1>1</x1>

<x2>hi</x2>

</xxx>

<xxx>

<x1>2</x1>

<x2>bye</x2>

</xxx>

<xxx>

<x1>3</x1>

<x2>no</x2>

</xxx>

</vijay>

<a2>10</a2>

</yyy>

 

The above example takes the concept of an array a step further. The class xxx contains two constructors, one having no parameters and the other with two parameters.

 

The class yyy has an array a1, which contains members of type xxx and not of type string. The three objects c, d and e are created as instances of xxx. The constructor of the two parameters in the class xxx initializes the two public variables a1 and a2, with the values in variables x1 and x2, respectively.

 

Then, an array 'f' of xxx objects is created and initialized to the three newly created xxx objects. Moreover, the a1 member of the yyy class is initialized to the array 'f'. This object is then serialized to the disk.

 

The attribute named XmlArrayAttribute alters the name of the array attribute from a1 to vijay in the xml file. As always, the xml file starts with the yyy root element. This is followed by the contents of the entire array being placed within the tags xxx, since xxx is the name of the array class. The individual members are placed with tags that are the names of the variables. The data types are conspicuous by their absence.

Thus, the class name xxx delimits each member of the array, whereas the variables in the class delimit the individual values. The member a2 of the yyy class is placed outside the tags of vijay.

 

There is a slight difference between an array of a built-in type and an array created from a user-defined class.  When the object is created from the file b1.xml using the Deserialize function, the arrays of the 3 xxx members are retained. They are displayed in the 'foreach' loop.

 

Error

Unhandled Exception: System.Exception: There was an error reflecting 'yyy'. ---> System.Exception: xxx cannot be serialized because it does not have a default public constructor.

 

The above exception is thrown when the constructor with no parameters is eliminated from the class xxx. This default constructor is mandatory whenever a class is to be serialized or written to an xml file on the disk.

 

a.cs

using System.Xml.Serialization;

using System.IO;

public class aaa : xxx

{

public string x3;

public aaa( int a, string b , string c)

{

x1 = a;

x2 = b;

x3 = c;

}

public aaa()

{

}

}

public class xxx

{

public int x1;

public string x2;

public xxx( int a, string b)

{

x1 = a;

x2 = b;

}

public xxx()

{

}

}

public class yyy

{

[XmlArrayItem(typeof(xxx)),XmlArrayItem(typeof(aaa))]

public xxx [] a1;

public int a2;

}

public class zzz

{

public static void Main()

{

XmlSerializer s = new XmlSerializer(typeof(yyy));

TextWriter w = new StreamWriter("b1.xml");

yyy b = new yyy();

xxx c = new xxx(1,"hi");

xxx d = new xxx(2,"bye");

aaa e = new aaa(3,"no","ok");

xxx [] f = { c,d,e };

b.a1 = f;

b.a2 = 10;

s.Serialize(w, b);

w.Close();

TextReader r = new StreamReader("b1.xml");

yyy a  = (yyy)s.Deserialize(r);

foreach( object s1 in a.a1)

{

if ( s1 is xxx)

{

xxx s2 = ( xxx ) s1;

System.Console.WriteLine(s2.x1 + " " + s2.x2);

}

if ( s1 is aaa)

{

aaa s2 = ( aaa ) s1;

System.Console.WriteLine(s2.x1 + " " + s2.x2 + " "  + s2.x3);

}

}

r.Close();

}

}

 

Output

1 hi

2 bye

3 no

3 no ok

 

The class aaa is derived from the class xxx. It has a three parameter constructor, which initializes the two members x1 and x2 from the class xxx and the member x3 from the class aaa. There also subsists an aaa constructor, which takes no parameters.

 

In the class yyy, the variable a1 is an array of type xxx, but has an attribute of XmlArrayItem added to it.

 

[XmlArrayItem(typeof(xxx)),XmlArrayItem(typeof(aaa))]

 

This attribute requisitions the serialization of both, an xxx type object and an aaa type object to an xml file, wherever an array of type xxx has been specified.

 

Earlier, the f array consisted of three xxx objects named b, c and d. However, now b and c are xxx objects, whereas e is an aaa object that is initialized using the three constructors of the aaa class. The creation of the array f generates no errors, because wherever an xxx object is specified, an aaa object can also be specified. It is due to the fact that aaa has been derived from the class xxx.

 

Thus, a derived and a base class can be used inter-changeably. The object, when written to the disk, displays the name of the array variable a1 with its three members. The first two are enveloped in an xxx tag and the third is located within an aaa element.

Displaying the yyy object 'a' is slightly more challenging,  since we want to isolate the aaa object stored in the xml file. In the 'foreach' statement, the variable s1 that receives the individual object is of type object and not of xxx. Then, using the 'is' keyword, the run time data type of the object s1 is verified.

 

If it is xxx, then the first 'if' statement is true and s1 is then typecast into an xxx object. Thereafter, its members are displayed. The second 'if' statement is true only if the object s1 has a runtime data type of aaa. In such a case, s2 is typecast to an aaa object, and then, the three members are displayed.

 

Error

Unhandled Exception: System.Exception: There was an error generating the XML document. ---> System.Exception: The type aaa was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.

 

On removing the two attributes of XmlArrayItem, which are placed over the array variable a1 in class yyy, the above exception gets generated. This error appears because, even though the type of the array is xxx, it is being stored in an aaa object instead. 

 

The aaa type gets displayed twice, because aaa is derived from yyy. Hence, it becomes a yyy type also. This results in the 'if' statement becoming true on two counts, once as a yyy type, and then as an xxx type.

 

Error

Unhandled Exception: System.InvalidOperationException: There was an error generating the XML document. ---> System.InvalidCastException: Specified cast is not valid.

 

When only the type aaa is specified, the above exception gets reported. Thus, it is mandatory for the array to mention all the types that it is capable of handling. However, it can exercise its discretion on the issue of mentioning its own type, since it is optional.

 

By using 'typeof 'in the XmlArrayItem attribute, a feature known as polymorphism gets implemented. This allows us to write a derived class to disk, where the array clearly stipulates that the type is a base class. We may use as many XmlArrayItem attributes as we desire.

 

[XmlArrayItem(typeof(xxx), ElementName = "ss"),XmlArrayItem(typeof(aaa),ElementName = "tt")]

 

b1.xml

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<a1>

<ss>

<x1>1</x1>

<x2>hi</x2>

</ss>

<ss>

<x1>2</x1>

<x2>bye</x2>

</ss>

<tt>

<x1>3</x1>

<x2>no</x2>

<x3>ok</x3>

</tt>

</a1>

<a2>10</a2>

</yyy>      

 

In the above example, the attribute definition is amended, wherein the property ElementName has been specified. For the type xxx, it has been set to ss. Hence, in the xml file, the xxx element is replaced by the name ss, while the aaa element is replaced by the name ss. The ElementName property for the type aaa is ignored.

 

However, there is an addition of the xsi:type attribute, which is used to indicate that this element could be an ss, but its runtime type is aaa. The xsi:type attribute has been demystified earlier.

 

 

a.cs

using System.Xml.Serialization;

using System.IO;

using System.Xml.Schema;

public class yyy

{

[XmlArrayItem(ElementName = "b1", NestingLevel = 0)]

[XmlArrayItem(ElementName = "b2", NestingLevel = 1)]

[XmlArrayItem(ElementName = "b3",NestingLevel = 2)]

public string[][][] a1;

}

public class zzz

{

public static void Main()

{

XmlSerializer s = new XmlSerializer(typeof(yyy));

TextWriter w = new StreamWriter("b1.xml");

yyy b = new yyy();

string[][][] aa = new string[2][][];

string[][] z1 = new string[2][];

z1 [0]=new string[1]{"vijay"};

z1 [1]=new string[2]{"hell","bad"};

aa[0]=z1;

string[][]z2= new string[2][];

z2[0]=new string[2]{"mukhi","sonal"};

z2[1]=new string[3]{"abc","def","ghi"};

aa[1]=z2;

b.a1=aa;

s.Serialize(w, b);

w.Close();

}

}

 

b1.xml

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<a1>

<b1>

<b2>

<b3>vijay</b3>

</b2>

<b2>

<b3>hell</b3>

<b3>bad</b3>

</b2>

</b1>

<b1>

<b2>

<b3>mukhi</b3>

<b3>sonal</b3>

</b2>

<b2>

<b3>abc</b3>

<b3>def</b3>

<b3>ghi</b3>

</b2>

</b1>

</a1>

</yyy>

 

The above example demonstrates an 'array of arrays of arrays', i.e. a three dimensional array. In the class yyy, a three-dimensional array a1 is created, with the final data type as string. Each dimension is assigned a name using the NestingLevel property. The three levels are b1, b2 and b3. The counting of the nesting levels starts with 0 and not with 1.

 

A variable aa of array type a1 is created. An array with all its dimensions cannot be created or initialized simultaneously. Firstly, the outermost dimension is handled, which has only two members, viz. aa[0] and aa[1]. These two members are initialized with an 'array of arrays' type. Two types called z1 and z2 are created. Then, an array of two arrays is created, taking into consideration the fact that only one dimension can be used at a given time.

 

The two z1 members z1[0] and z1[1] are then initialized to a single array containing one string and two strings, respectively. This signifies the fact that the two members of the array need not be of the same size.

A similar action is attempted for the second array of arrays z2; but here, an array of two strings and an array of three strings are involved. Finally, the a1 member is initialized to this 'array of arrays of arrays'.

 

The xml file commences with the first level or the NestingLevel 0 with the element b1. Then, the content vijay is present as part of the level 2 or the element b3. Thus, the element b2 or the Nesting level 1 has to come first. Here, the arrays of size one and two are posited. The b2 tag encompasses the array of two members within the b3 tag. The original b1 tag concludes, since the first main array has concluded.

 

Subsequent to this, we encounter the tags for the second array, which have two and three members internally. Here, the first b2 tag displays the first array and the second b2 displays the second array, which comprises of three members. The nested XML elements can be created upto any desired level. On eradicating the three XmlArrayItem attributes, the newly created xml file that results, is as follows:

 

b1.xml

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >

<a1>

<ArrayOfArrayOfString>

<ArrayOfString>

<string>vijay</string>

</ArrayOfString>

<ArrayOfString>

<string>hell</string>

<string>bad</string>

</ArrayOfString>

</ArrayOfArrayOfString>

<ArrayOfArrayOfString>

<ArrayOfString>

<string>mukhi</string>

<string>sonal</string>

</ArrayOfString>

<ArrayOfString>

<string>abc</string>

<string>def</string>

<string>ghi</string>

</ArrayOfString>

</ArrayOfArrayOfString>

</a1>

</yyy>

 

Although no errors are reported, it is exceedingly cumbersome to attempt reading. The a1 element still exists as before, but now the ArrayOfArrayOfString exists in place of the b1 tag. This is followed by an ArrayOfString tag in lieu of b2, and the final b3 is replaced with a string, which is the data type of the array.

 

[XmlArrayItem(ElementName = "b2", NestingLevel = 1)]

 

b1.xml

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >

<a1>

< ArrayOfArrayOfString >

<b2>

<string>vijay</string>

</b2>

<b2>

<string>hell</string>

<string>bad</string>

</b2>

</ArrayOfArrayOfString>

<ArrayOfArrayOfString>

<b2>

<string>mukhi</string>

<string>sonal</string>

</b2>

<b2>

<string>abc</string>

<string>def</string>

<string>ghi</string>

</b2>

</ArrayOfArrayOfString>

</a1>

</yyy>

 

Only one nesting level is given, and then, the name of b1 is specified. Thus, in the xml file, the second level is replaced by b2 and the ArrayOfArrayOfString and string tags remain unchanged. The default values of the nesting levels can be changed as per your requirement.

 

a.cs

using System.Xml.Serialization;

using System.IO;

public class yyy

{

[XmlElement(IsNullable= true)]

public string a1;

[XmlElement(IsNullable= false)]

public string a2;

}

public class zzz

{

public static void Main()

{

XmlSerializer s = new XmlSerializer(typeof(yyy));

TextWriter w = new StreamWriter("b1.xml");

yyy b = new yyy();

b.a1 = null;

b.a2 = null;

s.Serialize(w, b);

w.Close();

TextReader r = new StreamReader("b1.xml");

yyy a  = (yyy)s.Deserialize(r);

System.Console.WriteLine(a.a1 + " " + a.a2);

r.Close();

}

}

 

b1.xml

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<a1 xsi:nil="true" />

</yyy>

 

In the above example, the class yyy has two strings named a1 and a2. Both these strings are set to null. However, as per the xml file that is generated, only the a1 element is present and not a2. This is due to the XmlElement attribute, which sets the IsNullable attribute to true. This is the default value for a1.

 

Thus, if the value of the string a1 is null, then it shall be written in the xml file, but with the xsi:nil attribute set to true. However, if the value of this attribute is amended to false, then it becomes a null item, and null items are not present in the xml file. As a result, since the variable a2 is null, it is not flashed in the xml file.

 

a.cs

using System.Xml.Serialization;

using System.IO;

public class yyy

{

[XmlElement(Namespace="ttt")]

public string a1;

[XmlElement(Namespace="uuu")]

public string a2;

}

public class zzz

{

public static void Main()

{

XmlSerializer s = new XmlSerializer(typeof(yyy));

TextWriter w = new StreamWriter("b1.xml");

yyy b = new yyy();

b.a1 = "hi";

b.a2 = "bye";

s.Serialize(w, b);

w.Close();

TextReader r = new StreamReader("b1.xml");

yyy a  = (yyy)s.Deserialize(r);

System.Console.WriteLine(a.a1 + " " + a.a2);

r.Close();

}

}

 

Output

hi bye

 

b1.xml

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<a1 xmlns="ttt">hi</a1>

<a2 xmlns="uuu">bye</a2>

</yyy>

 

In the above C# program, two strings named a1 and a2 are placed in two different namespaces called ttt and uuu, respectively, using the property Namespace in the XmlElement attribute. No other amendment is carried out in the file. However, the xml file displays something absolutely different.

 

Using the xmlns attribute, the default namespace of the a1 element is changed to ttt. The same is true of the element a2, which has its default namespace set to uuu. Now, we are confronted with the poser as to what exactly do ttt and uuu represent ? The next example unveils this mystery.

 

a.cs

using System.Xml.Serialization;

using System.IO;

public class yyy

{

[XmlElement(Namespace="ttt")]

public string a1;

[XmlElement(Namespace="uuu")]

public string a2;

}

public class zzz

{

public static void Main()

{

XmlSerializer s = new XmlSerializer(typeof(yyy));

TextWriter w = new StreamWriter("b1.xml");

XmlSerializerNamespaces n = new XmlSerializerNamespaces();

n.Add("n1","ttt");

n.Add("n2","uuu");

yyy b = new yyy();

b.a1 = "hi";

b.a2 = "bye";

s.Serialize(w, b , n);

w.Close();

TextReader r = new StreamReader("b1.xml");

yyy a  = (yyy)s.Deserialize(r);

System.Console.WriteLine(a.a1 + " " + a.a2);

r.Close();

}

}

 

Output

hi  bye

 

b1.xml

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:n1="ttt" xmlns:n2="uuu">

<n1:a1>hi</n1:a1>

<n2:a2>bye</n2:a2>

</yyy>

 

We have created an instance of the class XmlSerializerNamespaces. This class contains all the XML namespaces and prefixes that the Serialize function requires to create fully qualified names. The Add function is used to add a prefix and a namespace combo to the XmlSerializerNamespaces object.

 

For those who tuned in late, the XML namespace is a mechanism to qualify names of elements or attributes, so that even when two elements are assigned the same name, they can be differentiated from one another.

 

Thus, a qualified name is merely a namespace prefix, followed by a colon and the local name, or the actual name of an element. In the above case, the prefixes n1 and n2 are inutile. They only act as placeholders for a URI or a Universal Resource Identifier.

 

A URL is different from a URI, since a URL points to an actual resource on the Net or elsewhere. This combination of a namespace prefix and a name is globally unique. Thus, in the xml file, since a1 originates from the namespace ttt, it is prefaced with n1. In the root tag, n1 points to the namespace ttt using the xmlns attribute.

 

The same is true  of a2, which uses n2, since n2 is associated with the namespace uuu by using the Add functions. An apt method of working with namespaces is by using the XmlSerializerNamespaces class.

 

a.cs

using System.Xml.Serialization;

using System.IO;

using System.Xml.Schema;

[XmlRootAttribute(Namespace="ttt")]

public class yyy

{

[XmlElement(Form=XmlSchemaForm.Unqualified)]

public string a1;

[XmlElement(Namespace="uuu" , Form=XmlSchemaForm.Qualified)]

public string a2;

}

public class zzz

{

public static void Main()

{

XmlSerializer s = new XmlSerializer(typeof(yyy));

TextWriter w = new StreamWriter("b1.xml");

XmlSerializerNamespaces n =  new XmlSerializerNamespaces();

n.Add("n1","ttt");

n.Add("n2","uuu");

yyy b = new yyy();

b.a1 = "hi";

b.a2 = "bye";

s.Serialize(w, b , n);

w.Close();

TextReader r = new StreamReader("b1.xml");

yyy a  = (yyy)s.Deserialize(r);

System.Console.WriteLine(a.a1 + " " + a.a2);

r.Close();

}

}

 

Output

hi bye

 

b1.xml

<?xml version="1.0" encoding="utf-8"?>

<n1:yyy xmlns:n2="uuu" xmlns:n1="ttt">

<a1>hi</a1>

<n2:a2>bye</n2:a2>

</n1:yyy>

 

The root element employs the Namespace property to set the namespace of the xml file to ttt. Moreover, the Form attribute is added to the variable a1 with the value of Unqualified from the enum XmlSchmaForm.

 

The namespace property has been eliminated, and the second string a2 is assigned the value of 'qualified'. The third value of 'none' is equivalent to 'qualified', and hence, its presence is optional; however, it has been placed here for the sake of consistency. The string a2 belongs to the namespace uuu, like before.

 

In the xml file, both the elements, i.e. a2 and yyy are qualified. The a1 element is not qualified, since it has been explicitly marked as 'unqualified'. The a2 element has to be qualified as it belongs to a different namespace. If the attribute from the variable a1 is placed within comments, it shall also appear with a namespace prefix.

 

Thus, the 'form' property determines whether an XML element is qualified or unqualified in the instance document. If the Namespace property is added to string a1, the following exception is thrown:

 

[XmlElement(Form=XmlSchemaForm.Unqualified , Namespace ="iii")]

public string a1;

 

Error

Unhandled Exception: System.InvalidOperationException: There was an error reflecting 'yyy'. ---> System.InvalidOperationException: The Form property may not be 'Unqualified' when an explicit Namespace property is present.

 

The above error was anticipated, since the element belonging to the namespace iii has to be qualified. This is in contrast to the Form property, which does not ask for it to be qualified. This is a contradiction that the Serialize function is unable to handle.

 

Just to refresh your memory, namespaces are inconsequential in the C# world, and thus, the Deserialize function does not require a Namespace parameter, while the Serialize function does.

 

a.cs

using System.Xml.Serialization;

using System.IO;

using System.Xml.Schema;

public class yyy

{

[XmlAttribute]

public string a1;

public string a2;

}

public class zzz

{

public static void Main()

{

XmlSerializer s = new XmlSerializer(typeof(yyy));

s.UnknownElement += new XmlElementEventHandler(abce);

s.UnknownNode += new XmlNodeEventHandler(abcn);

s.UnknownAttribute += new XmlAttributeEventHandler(abca);

TextReader r = new StreamReader("b1.xml");

yyy a  = (yyy)s.Deserialize(r);

System.Console.WriteLine(a.a1 + " " + a.a2);

r.Close();

}

static protected void abcn (object sender, XmlNodeEventArgs e)

{

System.Console.WriteLine("Node: " +   e.Name + " " + e.Text);

}

static protected void abce (object sender, XmlElementEventArgs  e)

{

System.Console.WriteLine("Element: " + e.Element.InnerXml + " " + e.Element.Name);

}

static protected void abca(object sender, XmlAttributeEventArgs e)

{

System.Xml.XmlAttribute a = e.Attr;

System.Console.WriteLine("Attribute: " +  a.Name + "=" + a.Value) ;

}

}

 

b1.xml (without errors)

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" a1="hi">

  <a2>bye</a2>

</yyy>

 

Output

hi bye

 

b1.xml (with errors)

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" a3="hi">

<a4>bye</a4>

</yyy>

 

Output

Node: a3 hi

Attribute: a3=hi

Node: a4

Element: bye a4

In the xml file, the attribute a1 is replaced by a3, while the element a2 is replaced by a4. Both, the correct and the erroneous XML files have been furnished. This corroborates the fact that the C# program tests the error-handling capabilities of the Deserialize function.

 

Three events of the XmlSerializer class are initialized. The UnknownElement event is pressed into action whenever the Deserialize function encounters an element that is not authorised. This triggers the calling of the abce function.

 

This function gets called only once at the node a4, since it expects the node a2 instead. The Name property of the XmlElement object is used, which is a property of the XmlElementEventArgs parameter. The InnerXml property of the XmlElement is used to display the contents of the element.

 

The UnknownNode gets called whenever an Unknown node is met. The function abcn gets called in such situations. This function is summoned twice since two nodes are out of place here, viz. a3 and a4. The first node has the value 'hi' and the second has the value null. The string 'hi' is the value of the attribute a1, which is now replaced by the node a3.  

 

An UnknownAttribute event is called whenever an unknown attribute is encountered. Therefore, the function abca gets called only once. This occurs because only one attribute is out of place.

 

The XmlAttributeEventArgs parameter has an Attr property, which provides an XmlAttribute object, out of which, only the Name and Value are displayed. Numerous other parameters can also be displayed on similar lines.

 

Attribute Overrides

 

a.cs

using System;

using System.IO;

using System.Xml.Serialization;

public class yyy

{

public string a1;

[XmlIgnoreAttribute]

public string a2;

}

public class zzz

{

public static void Main()

{

XmlAttributeOverrides  o = new XmlAttributeOverrides ();

XmlAttributes a = new XmlAttributes();

a.XmlIgnore = false;

o.Add(typeof(yyy), "a2", a);

a = new XmlAttributes();

a.XmlIgnore = true;

o.Add(typeof(yyy), "a1", a);

XmlSerializer s = new XmlSerializer(typeof(yyy), o);

yyy b = new yyy();

b.a1 = "vijay";

b.a2 = "mukhi";

TextWriter w = new StreamWriter("b1.xml");

s.Serialize(w, b);

w.Close();

TextReader r = new StreamReader("b1.xml");

yyy c  = (yyy)s.Deserialize(r);

System.Console.WriteLine(c.a1 + "." + c.a2);

r.Close();

}

}

 

Output

.mukhi

 

b1.xml

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<a2>mukhi</a2>

</yyy>

 

 

The class yyy has two variables a1 and a2, with the second variable a2 having an attribute of XmlIgnore. Thus, the xml file must only contain the element a1. However, the xml file exhibits just the reverse, in that it only contains a2, which it should not, while it does not contain a1, which it actually should.

 

Let us examine the class zzz in order to establish the cause behind this occurrence. The class XmlAttributeOverrides allows the overriding of all the attributes that have been specified on a class. Thus, this class dynamically takes a decision on the attribute that would finally be used during the creation of the xml file.

 

Firstly, an object 'a' of type XmlAttributes is created. This object has all the members of an attribute. Then, the XmlIgnore member is set to false. The Add function from the XmlAttributeOverrides class is then used to specify three parameters.

 

The first parameter is the type of object that the attribute applies to, which in our case is the good old yyy class. The second parameter is the name of the variable or property that the attribute must apply to. The third parameter is the XmlAttribute instance, whose members like XmlIgnore, have already been set.

 

The XmlAttributeOverrides class stores these three values together as a single entity. Scores of such entities can exist. The XmlIgnore attribute is set to true for the variable a1 in the class yyy.

 

Earlier, the XmlSerializer object had been created using a single parameter in the constructor; in this case however, the XmlAttributeOverrides object 'o' is also added as the second parameter. Prior to writing to the disk, the Serialize function first checks the variables or properties that have overridden the attributes. Therefore, even though the a1 variable should have been written to the disk, since the XmlIgnore has been set to true, this will not happen.

 

In the case of the variable a2, the reverse action takes place. Even though the XmlIgnore attribute is stated in the class, the field is not written to the disk. It is by reason of the fact that XmlIgnore is set to false in the Override for the variable a2. Thus, it is the Overrides attribute that ultimately determines what the xml file should look like, and no power on earth can override the contents of the XmlAttributeOverrides class.

 

The Deserialize finally displays 'mukhi' as the value of a2. The value of the element a1 does not exist, since it was not written to the disk.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Xml.Serialization;

using System.ComponentModel;

public class yyy

{

[DefaultValueAttribute("vijay")]

public string a1;

}

public class zzz

{

public static void Main()

{

XmlSerializer s = new XmlSerializer(typeof(yyy));

TextWriter w = new StreamWriter("b1.xml");

yyy a = new yyy();

a.a1 = "vijay";

s.Serialize(w, a);

w.Close();

TextReader r = new StreamReader("b1.xml");

yyy c  = (yyy)s.Deserialize(r);

System.Console.WriteLine(c.a1);

r.Close();

}

}

 

b1.xml

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />

 

The xml file should have contained the variable a1 along with the word 'vijay', but there is no trace of a1 anywhere in the xml file. So, let us amend a line in the above program.

 

a.a1 = "mukhi";

 

b1.xml

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >

<a1>mukhi</a1>

</yyy>

 

Output

mukhi

 

No sooner do we amend the value of the variable a1 to 'mukhi', the element a1 gets written to the disk with its  content as 'mukhi'. Thus, whenever the attribute of DefaultValueAttribute is used, and the same value is specified, this value does not get written to the xml file at all.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Xml.Serialization;

using System.ComponentModel;

public class yyy

{

public string a1;

}

public class zzz

{

public static void Main()

{

XmlAttributeOverrides o = new XmlAttributeOverrides();

XmlAttributes x = new XmlAttributes();

x.XmlDefaultValue = "vijay";

o.Add(typeof(yyy), "a1", x);

XmlSerializer s = new XmlSerializer(typeof(yyy), o);

TextWriter w = new StreamWriter("b1.xml");

yyy a = new yyy();

a.a1 = "vijay";

s.Serialize(w, a);

w.Close();

}

}

 

b1.xml

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />

 

The same output that was achieved by the earlier program is witnessed in the case of the above program. Here, the XmlDefaultValue property of the XmlAttributes class has been used. It depends upon your individual preference  as to whether you wish to use Attributes or the Overrider class to accomplish the same task.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Xml.Serialization;

using System.ComponentModel;

public class yyy

{

public string a1;

[XmlText]

public string a2;

public string a3;

}

public class zzz

{

public static void Main()

{

XmlSerializer s = new XmlSerializer(typeof(yyy));

TextWriter w = new StreamWriter("b1.xml");

yyy a = new yyy();

a.a1 = "vijay";

a.a2 = "mukhi";

a.a3 = "sonal";

s.Serialize(w, a);

w.Close();

TextReader r = new StreamReader("b1.xml");

yyy b  = (yyy)s.Deserialize(r);

System.Console.WriteLine(b.a1 + " " + b.a2 + " " + b.a3);

r.Close();

}

}

 

Output

vijay mukhi sonal

 

b1.xml

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<a1>vijay</a1>mukhi<a3>sonal</a3></yyy>

 

In the above program, there are three fields, viz. a1, a2 and a3. Each one of them has been initialized to a string value. Thus, the xml file is expected to have 3 tags or elements named a1, a2 and a3.

 

The file shows a1 containing 'vijay', as is expected. However, the a2 element is not present in the file. Any field or property that contains the attribute XmlText, simply evolves into text or content in the xml file.

 

Thus, instead of containing 'mukhi' within the element a2, it merely places the content in the xml file without the tags. The a3 element just follows the text 'mukhi'. However, on deserializing the file, the a2 field gets populated with 'mukhi', as if it were present as an element in the xml file.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Xml.Serialization;

using System.ComponentModel;

public class yyy

{

public xxx a1;

}

public enum xxx

{

sonal,

vijay

}

public class zzz

{

public static void Main()

{

XmlSerializer s = new XmlSerializer(typeof(yyy));

TextWriter w = new StreamWriter("b1.xml");

yyy a = new yyy();

s.Serialize(w, a);

a.a1 = xxx.sonal;

w.Close();

TextReader r = new StreamReader("b1.xml");

yyy b  = (yyy)s.Deserialize(r);

System.Console.WriteLine(a.a1);

r.Close();

}

}

 

Output

sonal

 

b1.xml

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<a1>vijay</a1>mukhi<a3>sonal</a3></yyy>

 

We have had a brief brush with enum earlier. It gets converted into a series of enumerations. In the above program, the class yyy has a field a1 of type enum xxx. This enum has two values, viz. vijay and sonal. A yyy object is created and the field a1 is initialized to sonal, using the enum name and the field xxx.sonal as follows: a.a1 = xxx.sonal;

 

However, the xml file contains no vestige of the enum; the single perceptible thing being the content sonal within the a1 tag. On examining the xml file closely, we discover that there is no indication of a1 belonging to type enum. The enum merely accomplishes conducting an error check.

 

The field a1 cannot assume values that are not the integral constituents of an enum. During deserialization, the system is aware that the data type is an enum and it works accordingly.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Xml.Serialization;

using System.ComponentModel;

public class yyy

{

public xxx a1;

}

public enum xxx

{

vijay,

sonal

}

public class zzz

{

public static void Main()

{

XmlSerializer s = new XmlSerializer(typeof(yyy));

yyy a = new yyy();

TextReader r = new StreamReader("b1.xml");

yyy b  = (yyy)s.Deserialize(r);

System.Console.WriteLine(a.a1);

r.Close();

}

}

 

b1.xml

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<a1>sonal1</a1>

</yyy>

 

Error

Unhandled Exception: System.InvalidOperationException: There is an error in XML document (4, 5). ---> System.InvalidOperationException: 'sonal1' is not a valid value for xxx.

 

The code that writes or serializes the xml file has been eradicated from the above program. The enum type a1 can take only the two values of 'sonal' and 'vijay', but we have distorted the value of 'sonal' in the xml file to 'sonal1'.

 

On executing the program, the above exception is generated, since sonal1 is not a valid value for the enum type a1. Thus, even at the time of de-serialization, a sizeable number of error checks are conducted.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Xml.Serialization;

using System.ComponentModel;

public class yyy

{

public xxx a1;

}

public enum xxx

{

vijay,

sonal

}

public class zzz

{

public static void Main()

{

XmlAttributeOverrides o = new XmlAttributeOverrides ();

XmlAttributes x = new XmlAttributes();

XmlEnumAttribute e = new XmlEnumAttribute();

e.Name = "mukhi";

x.XmlEnum = e;

o.Add(typeof(xxx), "sonal", x);

XmlSerializer s = new XmlSerializer(typeof(yyy),o);

yyy a = new yyy();

TextWriter w = new StreamWriter("b1.xml");

a.a1 = xxx.sonal;

s.Serialize(w, a);

w.Close();

TextReader r = new StreamReader("b1.xml");

yyy b  = (yyy)s.Deserialize(r);

System.Console.WriteLine(b.a1);

r.Close();

}

}

 

Output

sonal

 

b1.xml

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<a1>mukhi</a1>

</yyy>

 

The program has an XmlAttributeOverrides object 'o' and an XmlAttributes object. The XmlEnumAttribute class object 'e' is used to override an enum. The Name property is initialized to the value 'mukhi', which is then placed in the xml file.

 

 

Thereafter, the XmlEnum member of the XmlAttributes class is set to this freshly created enum object. Then, the Add function is used. Here, the first parameter specifies the data type of the xxx class, the second parameter specifies the name of the enum member that is to be replaced, and finally, the third parameter is the value that it is to be replaced with, viz. XmlEnumAttribute object 'e'.

 

In the xml file, the string 'mukhi' is visible in place of 'sonal', since the overrider has been called upon to replace 'sonal' with 'mukhi'. However, the Deserialize function pays no heed to the overrider, and even though 'mukhi' is present in the xml file, the value of the a1 enum is taken as 'vijay'.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Xml.Serialization;

using System.ComponentModel;

public class yyy

{

public xxx a1;

}

public enum xxx

{

vijay,

sonal

}

public class zzz

{

public static void Main()

{

XmlSerializer s = new XmlSerializer(typeof(yyy));

yyy a = new yyy();

TextReader r = new StreamReader("b1.xml");

yyy b  = (yyy)s.Deserialize(r);

System.Console.WriteLine(b.a1);

r.Close();

}

}

Error

Unhandled Exception: System.InvalidOperationException: There is an error in XML document (4, 5). ---> System.InvalidOperationException: 'mukhi' is not a valid value for xxx.

 

By retaining the b1.xml file as it is and on executing the above program, the above exception is generated.

 

The program resorts to the Deserialize function to read the file b1.xml. The above exception transpires since there is no indication of the fact that the string 'mukhi' stands for 'sonal'. Earlier, the XmlAttributeOverrides object 'o' had been passed as a parameter to the XmlSerializer class. This had taken care of the replacement.

 

However, there is no provision by which an enum can be added dynamically.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Xml.Serialization;

using System.ComponentModel;

public class xxx

{

public string a2;

}

public class vvv

{

public string a3;

}

public class yyy

{

public object [] a1;

}

public class zzz

{

public static void Main()

{

Type [] t = new Type[2];

t[0] = typeof(xxx);

t[1] = typeof(vvv);

XmlSerializer s = new XmlSerializer(typeof(yyy) , t);

TextWriter w = new StreamWriter("b1.xml");

yyy a = new yyy();

object [] o = new object[2];

xxx x = new xxx();

x.a2 = "vijay";

vvv v = new vvv();

v.a3 = "mukhi";

o[0] = x;

o[1] = v;

a.a1 = o;

s.Serialize(w, a);

w.Close();

TextReader r = new StreamReader("b1.xml");

yyy b  = (yyy)s.Deserialize(r);

object [] o1 = b.a1;

xxx x1 = (xxx) o1[0];

vvv v1 = (vvv) o1[1];

System.Console.WriteLine(x1.a2 + " " + v1.a3);

r.Close();

}

}

 

Output

vijay mukhi

 

b1.xml

<?xml version="1.0" encoding="utf-8"?>

<yyy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<a1>

<anyType xsi:type="xxx">

<a2>vijay</a2>

</anyType>

<anyType xsi:type="vvv">

<a3>mukhi</a3>

</anyType>

</a1>

</yyy>

 

As always, we start with the class yyy, which has the field a1 as an array of type object. In the C# world, all classes are finally derived from object. Thus, a1 can have members of any data type.

 

The classes xxx and vvv have the members a2 and a3, both of type string, respectively. An array of Type objects is created with the two members initialized to the types of xxx and vvv. This type array is then passed to the constructor of the XmlSerializer class.

 

Next, two objects x and v of types xxx and vvv, are created. Then, the members a2 and a3 are initialized to vijay and mukhi, respectively. Then, an array of objects named 'o' with a size of 2 is created, and the two members x and v are initialized. Ultimately, the a1 field is initialized to this array of objects, and subsequently, the Serialize function is called. This does not result in any errors.

 

In the xml file, the a1 element comes first, followed by the Object tag, since that is the data type of the array. The first array member is an xxx type.

 

Thus, the xsi:type member is set to this value. The content of the array is placed within the Object tag. The second array member is of type vvv and the xsi:type attribute contains this value. To discern this program more lucidly, the XmlSerializer constructor can be rewritten as follows:

 

XmlSerializer s = new XmlSerializer(typeof(yyy) );

 

When the second parameter is removed, which is the array of type objects, an exception gets thrown. This is depicted below.

 

Error

Unhandled Exception: System.InvalidOperationException: There was an error generating the XML document. ---> System.InvalidOperationException: The type xxx was n

ot expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.

This occurs because the system is called upon to write an object xxx to the disk, while the XmlSerializer class has only been supplied with the type yyy to be written to the disk. The types are furnished as an array of extra types in the constructor.