I am having some issues deserialising a mimeKit.mimeMessage that I serialised to a JSON string and stored in a redis key-value cache.
I am able to serialise and store the mimeMessage successfully using either json.NET or Jil, however when I go to deserialise, the following error is thrown.
Thrown by json.NET
Unable to find a constructor to use for type MimeKit.Header. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'Headers[0].Offset', line 1, position 22.
I am using the StackExchange.Redis method stringGet to get the serialised object and then passing the RedisValue into Json.Net; code snippet below:
RedisValue result = db.StringGet(key);
MimeMessage message = new MimeMessage();
message = JsonConvert.DeserializeObject<MimeMessage>(result);
It is my understanding that a default constructor is always created, which makes this a little hard to get my head around, could the default constructor have been specified as private, and if so why?
I have also checked the format of the returned JSON string and it is correct.
Thanks for your help.
First, to clear something up:
That's not true. When you define a class with no constructors, a default, no-args constructor is generated for you. If you supply a constructor, this default constructor is no longer generated. In other words, a class may not have a default constructor.
With that in mind, the
MimeKit.Headerclass provides 6 constructors, none of which take no arguments. Because of this, JSON.NET does not know how to instantiate this class, which is why the exception occurs.Since you don't have any control over the
MimeKit.Headerclass, one way to let JSON.NET know how to construct aHeaderinstance is to use a custom converter:Here, we're deserializing the
Headerclass into aJObjectfirst. Then, we're picking theHeaderIdandValueout of thatJObjectin order to create aHeaderinstance.A note here: I don't know much about this library. This may not be the best way to instantiate a
Header, and I could be accidentally discarding some information. I only did some very basic tests.Once you get past this issue, you'll run into another one having to do with property setters that don't accept
nullvalues.Specifically, the
ResentMessageIdandMimeVersionproperties have logic in the setters that throwArgumentExceptions if you supplynull. This is an issue, since those values can be null when theMimeMessageis instantiated.One workaround for this would be to create a
ContractResolverthat excludes those properties:This way, we're never going to accidentally pass
nullto the setter for one of these properties.A note here as well: I noticed that even when I ignored the
MimeVersionandResentMessageIdthey still got set if the headers included aMimeVersionand/orResentMessageIdheader. I'm guessing this is baked into theMimeMessagesomewhere, but again, I'm not totally familiar with this library.So to use the classes above (only when deserializing), you'd do this:
With some basic tests this seemed to work fine. You may end up running into some more issues in actual use, so no guarantees.
Hopefully this will at least get you started.