How can you serialize and deserialize the ImapMessageInfo object?

+1 vote
asked Mar 17, 2016 by mtaber (220 points)
edited Mar 17, 2016 by mtaber

I've tried in numerous ways to get this working. I have a ton of emails I'm downloading and I only want the header information for now because I need to do some statistics aggregation and preprocessing on the data.

I'm able to download the emails and I save the ImapMessageInfo object to disk by serializing it as a Json object with the Newtonsoft.Json library.

When I try to deserialize the file, I get a variety of errors, depending on the specifics of what I do. The vast majority of the errors point to the fact that you can't instantiate a number of the objects because they have internal constructors. As the classes are also sealed, I can't inherit from them and add my own constructor.

How do I get around this so I can store and deserialize these objects? I can see them fine on disk. I just can't load them.

Applies to: Rebex Secure Mail

1 Answer

0 votes
answered Mar 18, 2016 by Tomas Knopp (58,890 points)
edited Mar 23, 2016 by Tomas Knopp

Hello,

please try the imap.GetMessageHeaders method. It retrieves the mail headers into a stream and then you can serialize/deserialize the stream with the JSON.

Loading the deserialized stream back to the MailMessage object is easy, just use the MailMessage.Load method.

Here is a short example code:

Imap imap = new Imap();
imap.Connect("server", SslMode.Implicit);
imap.Login("user", "password");

imap.SelectFolder("INBOX");

// only retrieve the list of unique ID's
// it is possible to use Imap.Search method instead of GetMessageList as well
var messages = imap.GetMessageList(ImapListFields.UniqueId);
var messageInfo = messages[0];

// save the headers into stream that can be serialized with JSON
MemoryStream ms = new MemoryStream();
imap.GetMessageHeaders(messageInfo.UniqueId, ms);
    imap.Disconnect();

// now deserialize the stream via JSON
MemoryStream deserialized = ...;

// and then load it to MailMessage to work with the headers
MailMessage mail = new MailMessage();
mail.Load(deserialized);
// now work with the headers via
// mail.Headers property
commented Mar 18, 2016 by Tomas Knopp (58,890 points)
You are right that serializing the ImapMessageInfo class is not possible so the GetMessageHeaders method is the recommended workaround to retrieve the headers only.
commented Mar 22, 2016 by mtaber (220 points)
edited Mar 22, 2016 by mtaber
I did see this mechanism before, but the problem is that it takes a round-trip back to the server for each and every message you want to retrieve the headers for. I'm implementing this for tens of thousands of emails and am downloading emails in batches. I don't want to make an individual request for each header because of the latency overhead and the high number of individual requests.

There are several different mechanisms for pulling data from the server for multiple messages. I'm specifically using the Search to pull back an ImapMessageCollection, which is also what GetMessageList does.

Is there a way to write the header information for multiple messages which are already downloaded back to disk or no?

Also, I tested saving these messages using both MailFormat.Mime and MailFormat.OutlookMsg. The OutlookMsg was unreadable via an editor, while the Mime format appeared to lose some of the information I wanted to get access to easily. This was the whole reason I wanted to save it as ImapMessageInfo format to begin with.
commented Mar 23, 2016 by Tomas Knopp (58,890 points)
edited Mar 23, 2016 by Tomas Knopp
You are right that my code was suboptimal. I focused on providing a working serialization/deserialization code and did not care so much about data traffic efficiency. However, a more efficient solution (that only downloads the headers from the IMAP server once) would be to call

    imap.GetMessageList(ImapListFields.UniqueId);

in my original code. This way you only retrieve the list of message id's. For each ID you can later retrieve the headers in a separate request.

if you prefer to have it done in one huge command, then another possibility is to download full headers of all messages at once from the IMAP folder using this code:

            var list = imap.GetMessageList(ImapListFields.FullHeaders);
            foreach (var info in list)
            {
                var mime = new MimeMessage();
                foreach (MimeHeader header in info.Headers)
                {
                    mime.Headers.Add(header);
                }
                mime.Save(...);
            }

Saving the headers to MimeMessage as demonstrated above is the preferred way then. You can later save the MimeMessage to stream or file and serialize / deserialize the header information without data loss.


Let me know whether this is more suitable. I updated my original code in the first answer to only retrieve the unique ID's. The Search method can be used instead of GetMessageList if you prefer that.
commented Mar 23, 2016 by Tomas Knopp (58,890 points)
MailMessage is a high-level class that can normalize the headers as described at http://www.rebex.net/secure-mail.net/features/mime-mailmessage.aspx#load-save . For loading/saving the headers as an .EML email file without modifications which seems to be what you need, please use the low-level MimeMessage class.

See http://www.rebex.net/secure-mail.net/features/mime-mailmessage.aspx#mail-mime-api
...