How to Serialize a MailMessage

Photo taken from http://www.willamalane.org/images/seniors/emailicon.jpg Last year I had written an SMTP component for .NET 2.0 and called it Gopi. Gopi is a simple library that lets you manage your emailing system in a .NET application and use SQL Server database as storage system for your data to make sure nothing will be missed.

Like many other libraries that are out there, Gopi uses different database columns for storing its data. There is a drawback with this approach and that is the limitations of storing all properties of an email into database easily. One obvious way is to do a lot of work and write many codes with various columns to store several properties but this isn't acceptable from a quality view and yields dirty code.

Prior to Community Server 2007 Telligent was using the same approach to store emails in Community Server but in this new version Ken Robertson came up with a nice technique on serializing an email and storing it in the database. This is a very nice and clever approach in my opinion and I liked it so much.

Background

For a new project that requires professional fundamental for everything I had to implement an emailing system so thought that it would be great to revisit my Gopi component and update with for .NET 3.5 and apply this approach.

In words this is so easy to implement because at first glance you think it's possible to serialize MailMessage object like a normal object and store it in database but when you want to do this some red errors come up and stop you from thinking because none of MailMessage classes are serializable with built-in serializers.

Obviously the solution is finding a way to serialize this class and this can be done by serializing everything by hand!

There are two MailMessage classes in .NET. First one is in System.Web.Mail namespace and is obsolete in .NET 2.0 and later and the second one is available in System.Net.Mail. I don't care about the obsolete one so I just show how you can serialize instances of System.Net.Mail.MailMessage class.

Class Diagram To serialize the properties of a MailMessage object you can create a new class and create a property of MailMessage type for it that embeds your MailMessage in the class. In this new class you can implement IXmlSerializable interface to manually serialize its MailMessage. Here I create this class and call it SerializableMailMessage.

In a similar approach you can derive your class from MailMessage and implement IXmlSerializable interface rather than embedding the MailMessage object in your class but I'd prefer the first method that makes things simpler when using the class.

IXmlSerializable interface has three methods to implement and you may know about this interface.

Here I'm going to use these two latter methods to provide serialization and deserialization features for my SerializableMailMessage class.

Serialization

Serialization side of the work is as simple as implementing the WriteXml method. The below code is what I've written to do this.

public void WriteXml(XmlWriter writer)

{

    if (this != null)

    {

        writer.WriteStartElement("MailMessage");

        writer.WriteAttributeString("Priority", Convert.ToInt16(this.Priority).ToString());

        writer.WriteAttributeString("IsBodyHtml", this.IsBodyHtml.ToString());

 

        // From

        writer.WriteStartElement("From");

        if (!string.IsNullOrEmpty(this.From.DisplayName))

            writer.WriteAttributeString("DisplayName", this.From.DisplayName);

        writer.WriteRaw(this.From.Address);

        writer.WriteEndElement();

 

        // To

        writer.WriteStartElement("To");

        writer.WriteStartElement("Addresses");

        foreach (MailAddress address in this.To)

        {

            writer.WriteStartElement("Address");

            if (!string.IsNullOrEmpty(address.DisplayName))

                writer.WriteAttributeString("DisplayName", address.DisplayName);

            writer.WriteRaw(address.Address);

            writer.WriteEndElement();

        }

        writer.WriteEndElement();

        writer.WriteEndElement();

 

        // CC

        if (this.CC.Count > 0)

        {

            writer.WriteStartElement("CC");

            writer.WriteStartElement("Addresses");

            foreach (MailAddress address in this.CC)

            {

                writer.WriteStartElement("Address");

                if (!string.IsNullOrEmpty(address.DisplayName))

                    writer.WriteAttributeString("DisplayName", address.DisplayName);

                writer.WriteRaw(address.Address);

                writer.WriteEndElement();

            }

            writer.WriteEndElement();

            writer.WriteEndElement();

        }

 

        // Bcc

        if (this.Bcc.Count > 0)

        {

            writer.WriteStartElement("Bcc");

            writer.WriteStartElement("Addresses");

            foreach (MailAddress address in this.Bcc)

            {

                writer.WriteStartElement("Address");

                if (!string.IsNullOrEmpty(address.DisplayName))

                    writer.WriteAttributeString("DisplayName", address.DisplayName);

                writer.WriteRaw(address.Address);

                writer.WriteEndElement();

            }

            writer.WriteEndElement();

            writer.WriteEndElement();

        }

 

        // Subject

        writer.WriteStartElement("Subject");

        writer.WriteRaw(this.Subject);

        writer.WriteEndElement();

 

        // Body

        writer.WriteStartElement("Body");

        writer.WriteCData(Body);

        writer.WriteEndElement();

 

        writer.WriteEndElement();

    }

}

There are a few points to mention about this code:

I stored the numeric value of MailPriority enumerator because it's easier to retrieve from the XML in deserialization. I also worked on common properties of MailMessage and didn't serialize all properties to keep it simple. Following same approach you can serialize other properties like Attachments. Next week I'll release the new version of Gopi that contains the updated code that serializes everything so you can wait shortly to get your hands on the code.

Deserialization

In the reverse direction I need to you need to implement ReadXml method to deserialize a MailMessage. The code is shown below.

public void ReadXml(XmlReader reader)

{

    XmlDocument xml = new XmlDocument();

    xml.Load(reader);

 

    // Properties

    XmlNode rootNode = GetConfigSection(xml, "SerializableMailMessage/MailMessage");

    this.IsBodyHtml = Convert.ToBoolean(rootNode.Attributes["IsBodyHtml"].Value);

    this.Priority = (MailPriority)Convert.ToInt16(rootNode.Attributes["Priority"].Value);

 

    // From

    XmlNode fromNode = GetConfigSection(xml, "SerializableMailMessage/MailMessage/From");

    string fromDisplayName = string.Empty;

    if (fromNode.Attributes["DisplayName"] != null)

        fromDisplayName = fromNode.Attributes["DisplayName"].Value;

    MailAddress fromAddress = new MailAddress(fromNode.InnerText, fromDisplayName);

    this.From = fromAddress;

 

    // To

    XmlNode toNode = GetConfigSection(xml, "SerializableMailMessage/MailMessage/To/Addresses");

    foreach (XmlNode node in toNode.ChildNodes)

    {

        string toDisplayName = string.Empty;

        if (node.Attributes["DisplayName"] != null)

            toDisplayName = node.Attributes["DisplayName"].Value;

        MailAddress toAddress = new MailAddress(node.InnerText, toDisplayName);

        this.To.Add(toAddress);

    }

 

    // CC

    XmlNode ccNode = GetConfigSection(xml, "SerializableMailMessage/MailMessage/CC/Addresses");

    foreach (XmlNode node in ccNode.ChildNodes)

    {

        string ccDisplayName = string.Empty;

        if (node.Attributes["DisplayName"] != null)

            ccDisplayName = node.Attributes["DisplayName"].Value;

        MailAddress ccAddress = new MailAddress(node.InnerText, ccDisplayName);

        this.CC.Add(ccAddress);

    }

 

    // Bcc

    XmlNode bccNode = GetConfigSection(xml, "SerializableMailMessage/MailMessage/Bcc/Addresses");

    foreach (XmlNode node in bccNode.ChildNodes)

    {

        string bccDisplayName = string.Empty;

        if (node.Attributes["DisplayName"] != null)

            bccDisplayName = node.Attributes["DisplayName"].Value;

        MailAddress bccAddress = new MailAddress(node.InnerText, bccDisplayName);

        this.Bcc.Add(bccAddress);

    }

 

    // Subject

    XmlNode subjectNode = GetConfigSection(xml, "SerializableMailMessage/MailMessage/Subject");

    this.Subject = subjectNode.InnerText;

 

    // Body

    XmlNode bodyNode = GetConfigSection(xml, "SerializableMailMessage/MailMessage/Body");

    this.Body = bodyNode.InnerText;

}

Above code uses a helper method to get an XmlNode for different sections.

public XmlNode GetConfigSection(XmlDocument xml, string nodePath)

{

    return xml.SelectSingleNode(nodePath);

}

This code is self-explanatory and doesn't need any comment but note that you need to update XPath expressions for your class names. Of course, I could use reflection to make this code more general but sometimes I'm a bad guy!!

Test

Alright! Now I can serialize my MailMessage objects easily. I can test this class in a console application that creates an instance of this class and serializes this object and stores its data to an XML file and displays it in the console then deserializes the data in the XML file.

class Program

{

    private const string path = @"D:\file.xml";

 

    static void Main(string[] args)

    {

        Console.Title = "Serialize MailMessage";

 

        MailMessage email = new MailMessage();

        email.From = new MailAddress("noreply@nayyeri.net", "Keyvan Nayyeri");

 

        email.To.Add(new MailAddress("test1@test.com", "Test1"));

        email.To.Add(new MailAddress("test2@test.com"));

        email.CC.Add(new MailAddress("test3@test.com", "Test3"));

        email.Bcc.Add(new MailAddress("test4@test.com"));

 

        email.Subject = "Test Email";

        email.Body = "<p>This is a test email for serialization!</p>";

 

        email.IsBodyHtml = true;

        email.Priority = MailPriority.High;

 

        SerializableMailMessage mailToSerialize = new SerializableMailMessage();

        mailToSerialize.Email = email;

 

        XmlSerializer serializer = new XmlSerializer(typeof(SerializableMailMessage));

 

        using (TextWriter streamWriter = new StreamWriter(path))

        {

            TextWriter writer = Console.Out;

            serializer.Serialize(writer, mailToSerialize);

            serializer.Serialize(streamWriter, mailToSerialize);

        }

 

        SerializableMailMessage mailToDeserialize = new SerializableMailMessage();

 

        using (TextReader streamReader = new StreamReader(path))

        {

            mailToDeserialize = serializer.Deserialize(streamReader)

                as SerializableMailMessage;

        }

 

        Console.ReadLine();

    }

}

This code generates an XML like this:

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

<SerializableMailMessage>

  <MailMessage Priority="2" IsBodyHtml="True">

    <From DisplayName="Keyvan Nayyeri">noreply@nayyeri.net</From>

    <To>

      <Addresses>

        <Address DisplayName="Test1">test1@test.com</Address>

        <Address>test2@test.com</Address>

      </Addresses>

    </To>

    <CC>

      <Addresses>

        <Address DisplayName="Test3">test3@test.com</Address>

      </Addresses>

    </CC>

    <Bcc>

      <Addresses>

        <Address>test4@test.com</Address>

      </Addresses>

    </Bcc>

    <Subject>Test Email</Subject>

    <Body><![CDATA[<p>This is a test email for serialization!</p>]]></Body>

  </MailMessage>

</SerializableMailMessage>

Putting a breakpoint at the last line of the code and using a visualizer I can also check my deserialization process.

Quick Watch

That's it! You can download the source code sample for this post from here.

[advertisement] Axosoft OnTime 2008 is four developer tools in one: bug tracking, project wiki, feature management, and help desk. It manages your development process so developers can focus on coding. Installed or Hosted – Free Single-user license -- Free 30-day team trial.

5 Comments : 02.29.08

Feedbacks

 avatar
#1
David Barrett
03.27.2008 @ 2:51 PM

Great article!

 avatar
#2
Tudor
04.17.2008 @ 12:40 PM

Thanks for the article. I've been trying to figure out a way to serialize a MailMessage. Didn't know it was that simple!

 avatar
#3
Agha usman
05.07.2008 @ 1:34 AM

Great Article ... but there is one issue .. what if somebody does not want to send BCC or CC in this case above code of deserialization fails.

I myself spent one day sorting out why deserialization is fail. the answer I got is to put a null check on CC like following

XmlNode ccNode = GetConfigSection(xml, "SerializableMailMessage/MailMessage/CC/Addresses");

if (ccNode != null ){

foreach (XmlNode node in ccNode.ChildNodes)

{

string ccDisplayName = string.Empty;

if (node.Attributes["DisplayName"] != null)

ccDisplayName = node.Attributes["DisplayName"].Value;

MailAddress ccAddress = new MailAddress(node.InnerText, ccDisplayName);

this.CC.Add(ccAddress);

}

}

and same as for BCC.

cheers

 avatar
#4
The Danish Dynamo
06.21.2008 @ 2:00 PM

Nice work - great naming - simple and easy to adopt.

 avatar
#5
My Best Blog Posts in 2008
12.31.2008 @ 1:40 PM

In the past 3.5 years of blogging, I haven’t had such best pick up collections in the end of the year, but now that everybody is writing one, why shouldn’t I write my own?! Collecting this list, I could realize some interesting facts that completely changed

Leave a Comment