Saturday, 7 June 2008

XML serialization in C#.NET

Have you ever tried to parse XML using the .NET framework? I have, and I can never remember the right/wrong way to do it. I normally spend hours on the internet before finding the same solution that I found during my previous attempt. Not anymore! Instead of interacting with the XML directly, convert it to an object that can be easily manipulated. This post shows how to deserialize an XML string into an object and serialize it back into an XML string using attributes from the System.Xml.Serialization namespace in the target class. The important ones that I've highlighted are XmlRoot, XmlAttribute and XmlElement. They're pretty self explanitory - they basically map a class to a root node, a class member to an attribute within a node and a class member to an element node respectively. When declaring these attributes, you specify the name of the attribute/node used in the string XML to establish the relationship. If your class uses exactly the same naming convention as the XML structure, you could (in theory) leave out these attributes. In most cases, the 2 naming conventions differ. I would recommend using them regardless because it makes your application more future proof. If the name of an element node changes, you only have to update the corresponding attribute instead of renaming a class member which could cause problems elsewhere (in the same or even other applications).

This example includes a utility class with 2 important methods - DeserializeObject and SerializeObject. Both use generics so you pass the target class when calling either method. The XML sample can be deserialized to create a Person object. Alternatively, an existing Person object can be serialized to create a similar XML structure.

Utility class:

using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

public class XMLSerializationUtility
{
public static T DeserializeObject<T>( Encoding encoding, string xml )
{
try
{
using (MemoryStream memoryStream = new MemoryStream( StringToByteArray( encoding, xml ) ) )
{
using ( XmlTextWriter xmlTextWriter = new XmlTextWriter( memoryStream, encoding ) )
{
XmlSerializer xmlSerializer = new XmlSerializer( typeof( T ) );

return (T)xmlSerializer.Deserialize( memoryStream );
}
}
}
catch
{
return default( T );
}
}

public static string SerializeObject<T>( Encoding encoding, T obj )
{
try
{
MemoryStream memoryStream = new MemoryStream();

using ( XmlTextWriter xmlTextWriter = new XmlTextWriter( memoryStream, encoding ) )
{
XmlSerializer xmlSerializer = new XmlSerializer( typeof( T ) );
xmlSerializer.Serialize( xmlTextWriter, obj );

memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
}

return ByteArrayToString( encoding, memoryStream.ToArray() );
}
catch
{
return string.Empty;
}
}

private static Byte[] StringToByteArray( Encoding encoding, string xml )
{
return encoding.GetBytes( xml );
}

private static string ByteArrayToString( Encoding encoding, byte[] byteArray )
{
return encoding.GetString( byteArray );
}
}

Person class:

using System;
using System.Xml.Serialization;

[XmlRoot( "person" ) ]
public class Person
{
private Guid _id;

[XmlAttribute( "id" )]
public Guid ID;
{
get { return _id; }
set { _id = value; }
}

private string _name;

[XmlElement( "Name" )]
public string Name;
{
get { return _name; }
set { _name = value; }
}

private DateTime _dateOfBirth;

[XmlElement( "dob" )]
public DateTime DateOfBirth;
{
get { return _dateOfBirth; }
set { _dateOfBirth = value; }
}

}

XML:

<?xml version="1.0" encoding="utf-8"?>
<person id="0ADD2974-B14E-440B-B435-C0AF65E57ACF">
<name>Andrew Gunn</name>
<dob>1985-08-08T12:00:00Z</dob>
</person>

Deserialize string XML into a Person object:
Person person = XMLSerializationUtility.DeserializeObject<Person>( Encoding.UTF8, xml );

Serialize a Person object into string XML:
string xml = XMLSerializationUtility.SerializeObject<Person>( Encoding.UTF8, Person );

12 comments:

Anonymous said...

thank u r information

it very useful

u r blog Is very nice

Anonymous said...

Like you said, serialization is a recurrent need, and you always need to look again to solve the same problem. Thank you for your solution.

Anonymous said...

First of all, thank you for your blog. It helped me a lot

What if the XML is in this format.

quiz
-mc
---question
---answer
---answer
-mc
quiz

The example above shows that answer element can be repeated.
how will i be able to serialize and deserialize it with this format. Thank you

Andrew Gunn said...

Try serialising the following class:

[XmlRoot("quiz")]
public class Quiz
{
[XmlElement("question")]
public string Question { get; set; }

[XmlElement("answer")]
public string[] Answers { get; set; }
}

I've simply given a string array an XmlElementAttribute which seems to do the trick.

Anonymous said...

Thanks for the info; I'm rather new to .net, so I apologize if this is a basic question...I am trying to use serialization to create an RSS feed of a company phone book. I have multiple classes based on an rss document structure, so can I use serialization across multiple classes to create 1 xml document?
this is my class structure:

rssdocument
--string version
--rsschannel channel

rsschannel
--string description
--string link
--title
--rsschannelitem[] item
..
..
..

rsschannel item
--string description
--string link
--string title
...
...
... and so on.

Any guidance appreciated!!!

Thanks
Gaya

Dennis Gorelik said...

Andrew -- your code example was a great help!

I fixed couple of issues with this code:
1) This line is useless:
using ( XmlTextWriter xmlTextWriter = new XmlTextWriter( memoryStream, encoding ) )
2) Deserializing "prohibited" characters.

See
corrected version of serialization/deserialization

Thanks!

Dennis Gorelik said...

What do you use this serialization/deserialization for?

I use it for calculating blobs that speed up pages load on my web site PostJobFree.com

Anonymous said...

thanks. works perfectly

Dennis Gorelik said...

Andrew,

Does it matter what Encoding you use for serialization?
In your code the results are going to string object anyway, and, if I'm not mistaken, .NET string is stored in the same Encoding no matter what you feed into it.

Right?

Andrew Gunn said...

Dennis, sorry for the late reply.

I use it for various things at work, mainly our new ReST API at Esendex. We do a lot of serialisation from our business models to XML and vice versa within our C# SDK. We've found it to be the most easiest and time effective solution.

With regards to the encoding, I'm not sure how to answer your question LOL. The code on my blog post is seriously out-of-date so I'll try and get that sorted ASAP. I'll also post some tests that I wrote for various encoding scenerios. This might shed some light.

Dennis Gorelik said...

It would be great to see updated version of this code and testing examples.

Anonymous said...

Thank you, this is very useful for the .NET newbie like me.