First, to clear something up:
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?
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 a Header
instance is to use a custom converter:
public class MimeHeaderConverter : JsonConverter
{
public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
JObject obj = serializer.Deserialize<JObject>(reader);
HeaderId headerId = obj["Id"].ToObject<HeaderId>();
Header header = new Header(headerId, obj["Value"].ToObject<string>());
return header;
}
public override void WriteJson(
JsonWriter writer,
object value,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type t)
{
return typeof(Header).IsAssignableFrom(t);
}
public override bool CanRead { get { return true; } }
public override bool CanWrite { get { return false; } }
}
Here, we're deserializing the Header
class into a JObject
first. Then, we're picking the HeaderId
and Value
out of that JObject
in order to create a Header
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
and MimeVersion
properties have logic in the setters that throw ArgumentException
s if you supply null
. This is an issue, since those values can be null when the MimeMessage
is instantiated.
One workaround for this would be to create a ContractResolver
that excludes those properties:
public class MimeMessageContractResolver : DefaultContractResolver
{
private static HashSet<string> excludedMembers = new HashSet<string>
{
"ResentMessageId",
"MimeVersion"
};
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
var baseMembers = base.GetSerializableMembers(objectType);
if (typeof(MimeMessage).IsAssignableFrom(objectType))
{
baseMembers.RemoveAll(m => excludedMembers.Contains(m.Name));
}
return baseMembers;
}
}
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
and ResentMessageId
they still got set if the headers included a MimeVersion
and/or ResentMessageId
header. I'm guessing this is baked into the MimeMessage
somewhere, but again, I'm not totally familiar with this library.
So to use the classes above (only when deserializing), you'd do this:
string someJsonString = "{ ... }";
MimeMessage deserialized = JsonConvert.DeserializeObject<MimeMessage>(
someJsonString, new JsonSerializerSettings
{
ContractResolver = new MimeMessageContractResolver(),
Converters = new JsonConverter[]
{
new MimeHeaderConverter()
}
});
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.