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.Header
class 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.Header
class, one way to let JSON.NET know how to construct aHeader
instance is to use a custom converter:Here, we're deserializing the
Header
class into aJObject
first. Then, we're picking theHeaderId
andValue
out of thatJObject
in order to create aHeader
instance.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
null
values.Specifically, the
ResentMessageId
andMimeVersion
properties have logic in the setters that throwArgumentException
s if you supplynull
. This is an issue, since those values can be null when theMimeMessage
is instantiated.One workaround for this would be to create a
ContractResolver
that excludes those properties:This way, we're never going to accidentally pass
null
to the setter for one of these properties.A note here as well: I noticed that even when I ignored the
MimeVersion
andResentMessageId
they still got set if the headers included aMimeVersion
and/orResentMessageId
header. I'm guessing this is baked into theMimeMessage
somewhere, 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.