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.