Using XML and XmlSerializer to persist some data category 'KB', language C#, created 28-Oct-2010, version V1.0, by Luc Pattyn |
|
License: The author hereby grants you a worldwide, non-exclusive license to use and redistribute the files and the source code in the article in any way you see fit, provided you keep the copyright notice in place; when code modifications are applied, the notice must reflect that. The author retains copyright to the article, you may not republish or otherwise make available the article, in whole or in part, without the prior written consent of the author. Disclaimer: This work is provided “as is”, without any express or implied warranties or conditions or guarantees. You, the user, assume all risk in its use. In no event will the author be liable to you on any legal theory for any special, incidental, consequential, punitive or exemplary damages arising out of this license or the use of the work or otherwise. |
XML is a vast subject. This article will only scratch the surface; in particular it will illustrate how XML and XmlSerializer can be used for persisting some data.
Some useful links to MSDN:
Reference Material | Classes |
Introducing XML Serialization XML attributes | XmlSerializer |
XmlSerializer is the class that deals with XML serialization for serializable data types; these include:
When XmlSerializer.Serialize()
is called, all public data fields and properties
get serialized automatically to a text stream.
The data gets converted into strings by calling ToString()
and the result gets
HTML-escaped, i.e. some conversions get applied to prevent the XML scheme from breaking.
As an example [ < & > ]
gets replaced by [ < & > ]
.
Note: the order of class members may be changed; e.g. typically data members get serialized before properties.
The (de)serialization can be inlfuenced by adding attributes, see MSDN on
XML attributes.
One useful attribute is [XmlIgnore]
which excludes the data field from the
serialization process.
This example shows some value types and the string encoding; it also shows how null references are skipped:
// class to be serialized
public class ValueTypes {
public int myInt=123;
public DateTime myDate=DateTime.Now;
public Color myColor1=Color.FromArgb(0x11, 0x22, 0x33);
public Color myColor2=Color.Yellow;
public string myNullString=null;
public string myString="abc'.!@#$%^&*()<-_=+>qwerty</str>uiop";
public override string ToString() {
return "ValueTypes: myInt="+myInt+" myDate="+myDate+Environment.NewLine+
" myColor1="+myColor1+" myColor2="+myColor2+Environment.NewLine+
" myNullString="+myNullString+" myString="+myString;
}
}
// serializing code
using (StreamWriter sw=File.CreateText(FILEPATH)) {
XmlSerializer xSer=new XmlSerializer(typeof(ValueTypes));
xSer.Serialize(sw, new ValueTypes());
}
<?xml version="1.0" encoding="utf-8"?>
<ValueTypes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<myInt>123</myInt>
<myDate>2010-10-27T18:53:09.758+02:00</myDate>
<myColor1 />
<myColor2 />
<myString>abc'.!@#$%^&*()<-_=+>qwerty</str>uiop</myString>
</ValueTypes>
// deserializing code
using (StreamReader sr=File.OpenText(FILEPATH)) {
XmlSerializer xSer=new XmlSerializer(typeof(ValueTypes));
ValueTypes vt=(ValueTypes)xSer.Deserialize(sr);
log(vt.ToString());
}
// result
ValueTypes: myInt=123 myDate=28-Oct-2010 0:40:45
myColor1=Color [Empty] myColor2=Color [Empty]
myNullString= myString=abc'.!@#$%^&*()<-_=+>qwerty</str>uiop
This example demonstrates lists, where each item is an aggregation of two objects; the object hierarchy gets handled completely automatically:
// classes to be serialized
public class Mini {
public int val;
private Mini() { }
public Mini(int val) { this.val=val; }
public override string ToString() {return "Mini: val="+val;}
}
public class Combo {
public Mini Mini1;
public Mini Mini2;
private Combo() { }
public Combo(int val1, int val2) { Mini1=new Mini(val1); Mini2=new Mini(val2); }
public override string ToString() { return "Combo: Mini1.val="+Mini1.val+" Mini2.val="+Mini2.val; }
}
// serializing code
List<Mini> list=new List<Mini>();
for (int i=0; i<3; i++) list.Add(new Mini(i));
using (StreamWriter sw=File.CreateText(FILEPATH)) {
XmlSerializer xSer=new XmlSerializer(typeof(List<Mini>));
xSer.Serialize(sw, list);
}
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfCombo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Combo>
<Mini1>
<val>11</val>
</Mini1>
<Mini2>
<val>121</val>
</Mini2>
</Combo>
<Combo>
<Mini1>
<val>12</val>
</Mini1>
<Mini2>
<val>144</val>
</Mini2>
</Combo>
<Combo>
<Mini1>
<val>13</val>
</Mini1>
<Mini2>
<val>169</val>
</Mini2>
</Combo>
</ArrayOfCombo>
// deserializing code
using (StreamReader sr=File.OpenText(FILEPATH)) {
XmlSerializer xSer=new XmlSerializer(typeof(List<Mini>));
List<Combo> list=(List<Combo>)xSer.Deserialize(sr);
foreach (Combo combo in list) log(combo.ToString());
}
// result
Combo: Mini1.val=11 Mini2.val=121
Combo: Mini1.val=12 Mini2.val=144
Combo: Mini1.val=13 Mini2.val=169
So far the data was very predictable, it was either one object, or one collection of objects, all instances of a single class. When the collection isn't homogeneous, problems arise as the type information of the individual objects isn't serialized. Here is an example that fails:
// classes to be serialized
public class Base {}
public class DerivedA : Base {
public int A=1;
}
public class DerivedB : Base {
public int B=2;
}
// serializing code
using (StreamWriter sw=File.CreateText(FILEPATH)) {
List<Base> list=new List<Base>();
list.Add(new DerivedA());
list.Add(new DerivedB());
xSer=new XmlSerializer(typeof(List<Base>));
xSer.Serialize(sw, list); // InvalidOperationException
}
The XmlInclude
attribute can solve the problem if the derived classes are known
to the base class at build time; here
is an example.
And another XmlSerializer constructor (with "extraTypes") can solve
it for dynamic types, e.g. when supporting an add-on scheme.
It works great for single objects, and for homogeneous collections. And it gets harder for more dynamic situations. However it can't handle the Color type at all!
Perceler |
Copyright © 2012, Luc Pattyn |
Last Modified 02-Sep-2013 |