In this tutorial, I'm going to demonstrate how to
serialize your own objects to and from an XML file.
Since .NET can use reflection to get property names, basic serialization
is unbelievably simple. It only gets slightly difficult when you want to
name your XML tags differently than your property names (but still not
very hard). If you've ever used an XML serialization package in C++ like
boost, tinyXML, or libXML2, you'll see how comparatively easy C# is to
use.
Let's start with a basic example. Below is an object that stores some
information about a movie.
public class Movie
{
public string Title
{ get; set; }
public int Rating
{ get; set; }
public DateTime ReleaseDate
{ get; set; }
}
All right, now that we have an object, let's write a function that will
save it to XML.
static public void SerializeToXML(Movie movie)
{
XmlSerializer serializer = new XmlSerializer(typeof(Movie));
TextWriter textWriter = new StreamWriter(@"C:\movie.xml");
serializer.Serialize(textWriter, movie);
textWriter.Close();
}
The first thing I do is create an
XMLSerializer
(located in the System.Xml.Serialization namespace) that will serialize
objects of type Movie. The XMLSerializer will serialize objects to a
stream, so we'll have to create one of those next. In this case, I want
to serialize it to a file, so I create a TextWriter. I then simply call
Serialize on the XMLSerializer passing in the stream (textWriter) and
the object (movie). Lastly I close the TextWriter because you should
always close opened files. That's it! Let's create a movie object and
see how this is used.
static void Main(string[] args)
{
Movie movie = new Movie();
movie.Title = "Starship Troopers";
movie.ReleaseDate = DateTime.Parse("11/7/1997");
movie.Rating = 6.9f;
SerializeToXML(movie);
}
static public void SerializeToXML(Movie movie)
{
XmlSerializer serializer = new XmlSerializer(typeof(Movie));
TextWriter textWriter = new StreamWriter(@"C:\movie.xml");
serializer.Serialize(textWriter, movie);
textWriter.Close();
}
After this code executes, we'll have an XML file with the contents of
our movie object.
<?xml version="1.0" encoding="utf-8"?>
<Movie xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Title>Starship Troopers</Title>
<Rating>6.9</Rating>
<ReleaseDate>1997-11-07T00:00:00</ReleaseDate>
</Movie>
If you noticed, all of the XML tag names are the same as the property
names. If we want to change those, we can simply add an attribute above
each property that sets the tag name.
public class Movie
{
[XmlElement("MovieName")]
public string Title
{ get; set; }
[XmlElement("MovieRating")]
public float Rating
{ get; set; }
[XmlElement("MovieReleaseDate")]
public DateTime ReleaseDate
{ get; set; }
}
Now when the same code is executed again, we get our custom tag names.
<?xml version="1.0" encoding="utf-8"?>
<Movie xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MovieName>Starship Troopers</MovieName>
<MovieRating>6.9</MovieRating>
<MovieReleaseDate>1997-11-07T00:00:00</MovieReleaseDate>
</Movie>
Sometimes, in XML, you want information stored as an attribute of
another tag instead of a tag by itself. This can be easily accomplished
with another property attribute.
public class Movie
{
[XmlAttribute("MovieName")]
public string Title
{ get; set; }
[XmlElement("MovieRating")]
public float Rating
{ get; set; }
[XmlElement("MovieReleaseDate")]
public DateTime ReleaseDate
{ get; set; }
}
With this code, MovieName will now be an attribute on the Movie tag.
<?xml version="1.0" encoding="utf-8"?>
<Movie xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
MovieName="Starship Troopers">
<MovieRating>6.9</MovieRating>
<MovieReleaseDate>1997-11-07T00:00:00</MovieReleaseDate>
</Movie>
Let's move on to something a little more interesting. Let's create
another movie and serialize a List of them to our XML file. Here's the
modified code to do just that:
static void Main(string[] args)
{
Movie movie = new Movie();
movie.Title = "Starship Troopers";
movie.ReleaseDate = DateTime.Parse("11/7/1997");
movie.Rating = 6.9f;
Movie movie2 = new Movie();
movie2.Title = "Ace Ventura: When Nature Calls";
movie2.ReleaseDate = DateTime.Parse("11/10/1995");
movie2.Rating = 5.4f;
List<Movie> movies = new List<Movie>() { movie, movie2 };
SerializeToXML(movies);
}
static public void SerializeToXML(List<Movie> movies)
{
XmlSerializer serializer = new XmlSerializer(typeof(List<Movie>));
TextWriter textWriter = new StreamWriter(@"C:\movie.xml");
serializer.Serialize(textWriter, movies);
textWriter.Close();
}
Now we have XML that looks like this:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfMovie xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Movie MovieName="Starship Troopers">
<MovieRating>6.9</MovieRating>
<MovieReleaseDate>1997-11-07T00:00:00</MovieReleaseDate>
</Movie>
<Movie MovieName="Ace Ventura: When Nature Calls">
<MovieRating>5.4</MovieRating>
<MovieReleaseDate>1995-11-10T00:00:00</MovieReleaseDate>
</Movie>
</ArrayOfMovie>
Ok, so you can see how easy it is to get your objects into an XML
document. Let's now look at how to read an XML document back into our
objects - deserialization. The process of deserializing is very similar
to what we did for serialization.
static List<Movie> DeserializeFromXML()
{
XmlSerializer deserializer = new XmlSerializer(typeof(List<Movie>));
TextReader textReader = new StreamReader(@"C:\movie.xml");
List<Movie> movies;
movies = (List<Movie>)deserializer.Deserialize(textReader);
textReader.Close();
return movies;
}
Just like before, we first create an XmlSerializer that can deserialize
objects of type List. The XmlSerializer also deserializes from
a stream, so we create a file stream from our XML file. We then simply
call
Deserialize
on the stream and cast the output to our desired
type. Now the movies List is populated with objects that we previously
serialized to the XML file.
The deserializer is very good at handling missing pieces of information
in your XML file. Let's say the second movie didn't have the MovieName
attribute on the Movie tag. When the XML file is deserialized, it simply
populates that field with null. If MovieRating wasn't there, you'd
receive 0. Since a DateTime object can't be null, if MovieReleaseDate
was missing, you'd receive DateTime.MinValue (1/1/0001 12:00:00AM).
If the XML document contains invalid syntax, like say the first opening
Movie tag was missing, the Deserialize call will fail with an
InvalidOperationException. It will also be kind enough to specify the
location in the file where it encountered the error (line number, column
number).
One thing to remember is that the basic XML serialization won't maintain
references. Let's say I populated my movies list with the same movie
reference multiple times:
Movie movie = new Movie();
movie.Title = "Starship Troopers";
movie.ReleaseDate = DateTime.Parse("11/7/1997");
movie.Rating = 6.9f;
List<Movie> movies = new List<Movie>() { movie, movie };
Now I have a list containing two of the exact same movie reference. When
I serialize and deserialize this list, it will be converted to two
separate instances of the movie object - they would just have the same
information. Along this same line, the XMLSerializer also doesn't
support circular references. If you need this kind of flexibility, you
should consider binary serialization.
There's still a lot to cover when it comes to XML serialization, but I
think this tutorial covers enough of the basics to get things rolling.
Comments
Post a Comment