反序列化mimekit.MimeMessage对象

时间:2015-06-08 14:43:12

标签: c# json json.net mimekit

我有一些问题反序列化mimeKit.mimeMessage,我将其序列化为JSON字符串并存储在redis键值缓存中。

我能够使用json.NET或Jil成功地序列化和存储mimeMessage,但是当我去反序列化时,会抛出以下错误。

由json.NET抛出

  

无法找到用于MimeKit.Header类型的构造函数。一个类应该有一个默认的构造函数,一个带参数的构造函数或一个用JsonConstructor属性标记的构造函数。路径'标题[0]。偏移',第1行,第22位。

我使用StackExchange.Redis方法stringGet获取序列化对象,然后将RedisValue传递给Json.Net;下面的代码段:

RedisValue result = db.StringGet(key);

MimeMessage message = new MimeMessage();

message = JsonConvert.DeserializeObject<MimeMessage>(result);

我的理解是总是创建一个默认构造函数,这使得我有点难以理解,默认构造函数是否已被指定为私有,如果是,为什么?

我还检查了返回的JSON字符串的格式,它是正确的。

感谢您的帮助。

1 个答案:

答案 0 :(得分:5)

首先,澄清一些事情:

  

我的理解是总是创建一个默认构造函数,这使得我有点难以理解,默认构造函数是否已被指定为私有,如果是,为什么?

那不是真的。使用 no 构造函数定义类时,将为您生成缺省的no-args构造函数。如果提供构造函数,则不再生成此默认构造函数。换句话说,类可能没有默认构造函数。

考虑到这一点,MimeKit.Header类提供了6个构造函数,其中没有一个不带参数。因此,JSON.NET不知道如何实例化这个类,这就是异常发生的原因。

由于您对MimeKit.Header类没有任何控制权,让JSON.NET知道如何构建Header实例的一种方法是使用自定义转换器:

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; } }
}

在这里,我们首先将Header类反序列化为JObject。然后,我们从HeaderId中选择ValueJObject,以便创建Header个实例。

这里有一个注释:我对这个库不太了解。这可能不是实例化Header的最佳方式,我可能会意外丢弃某些信息。我只做了一些非常基本的测试。

一旦您解决了这个问题,您将遇到另一个与不接受null值的属性设置器有关的问题。

具体来说,如果您提供ResentMessageId,则MimeVersionArgumentException属性在设置者中具有抛出null的逻辑。这是一个问题,因为在实例化MimeMessage时,这些值可以为空。

这样做的一个解决方法是创建一个排除这些属性的ContractResolver

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;
    }
}

这样,我们永远不会意外地将null传递给其中一个属性的setter。

此处还有一条注释:我注意到,即使我忽略了MimeVersionResentMessageId,如果标题包含MimeVersion,我们仍会设置它们/或ResentMessageId标题。我猜这是在MimeMessage的某个地方,但同样,我并不完全熟悉这个库。

所以要使用上面的类(仅在反序列化时),你要这样做:

string someJsonString = "{ ... }";

MimeMessage deserialized = JsonConvert.DeserializeObject<MimeMessage>(
    someJsonString, new JsonSerializerSettings
    { 
        ContractResolver = new MimeMessageContractResolver(),
        Converters = new JsonConverter[] 
        { 
            new MimeHeaderConverter()
        }
    });

通过一些基本测试,这似乎工作正常。在实际使用中,您最终可能会遇到更多问题,因此无法保证。

希望这至少可以让你开始。