I've written some custom JsonConverters to deserialize json text into System.Net.Mail.MailMessage
objects. Here's the complete code, which can be run in LINQPad. Interestingly, this code runs as expected in Json.NET 4.5.11:
void Main()
{
const string JsonMessage = @"{
""From"": {
""Address"": ""askywalker@theEmpire.gov"",
""DisplayName"": ""Darth Vader""
},
""Sender"": null,
""ReplyTo"": null,
""ReplyToList"": [],
""To"": [
{
""Address"": ""lskywalker@theRebellion.org"",
""DisplayName"": ""Luke Skywalker""
}
],
""Bcc"": [],
""CC"": [
{
""Address"": ""lorgana@alderaan.gov"",
""DisplayName"": ""Princess Leia""
}
],
""Priority"": 0,
""DeliveryNotificationOptions"": 0,
""Subject"": ""Family tree"",
""SubjectEncoding"": null,
""Headers"": [],
""HeadersEncoding"": null,
""Body"": ""<strong>I am your father!</strong>"",
""BodyEncoding"": ""US-ASCII"",
""BodyTransferEncoding"": -1,
""IsBodyHtml"": true,
""Attachments"": [
{
""FileName"": ""skywalker family tree.jpg"",
""ContentBase64"": ""AQIDBAU=""
}
],
""AlternateViews"": []
}";
JsonConvert.DeserializeObject<MailMessage>(JsonMessage,
new MailAddressReadConverter(), new AttachmentReadConverter(), new EncodingReadConverter()).Dump();
}
public class MailAddressReadConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(MailAddress);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var messageJObject = serializer.Deserialize<JObject>(reader);
if (messageJObject == null)
{
return null;
}
var address = messageJObject.GetValue("Address", StringComparison.OrdinalIgnoreCase).ToObject<string>();
JToken displayNameToken;
string displayName;
if (messageJObject.TryGetValue("DisplayName", StringComparison.OrdinalIgnoreCase, out displayNameToken)
&& !string.IsNullOrEmpty(displayName = displayNameToken.ToObject<string>()))
{
return new MailAddress(address, displayName);
}
return new MailAddress(address);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public class AttachmentReadConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Attachment);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var info = serializer.Deserialize<AttachmentInfo>(reader);
var attachment = info != null
? new Attachment(new MemoryStream(Convert.FromBase64String(info.ContentBase64)), "application/octet-stream")
{
ContentDisposition = { FileName = info.FileName }
}
: null;
return attachment;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
private class AttachmentInfo
{
[JsonProperty(Required = Required.Always)]
public string FileName { get; set; }
[JsonProperty(Required = Required.Always)]
public string ContentBase64 { get; set; }
}
}
public class EncodingReadConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Encoding).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var encodingName = serializer.Deserialize<string>(reader);
return encodingName.NullSafe(s => Encoding.GetEncoding(s));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
The exception hit is:
System.ArgumentNullException : Value cannot be null.
at System.RuntimeType.MakeGenericType(Type[] instantiation)
at Newtonsoft.Json.Serialization.JsonArrayContract.CreateWrapper(Object list)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonConverter[] converters)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, JsonConverter[] converters)
Is this a bug in JSON 6? Am I doing something wrong?
EDIT: through further debugging, I've determined that the issue is the Headers
property.