简体   繁体   English

反序列化mimekit.MimeMessage对象

[英]Deserialising mimekit.MimeMessage Object

I am having some issues deserialising a mimeKit.mimeMessage that I serialised to a JSON string and stored in a redis key-value cache. 我在反序列化mimeKit.mimeMessage时遇到一些问题,该序列已序列化为JSON字符串并存储在redis键值缓存中。

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. 我能够使用json.NET或Jil成功地序列化并存储mimeMessage,但是当我进行反序列化时,会引发以下错误。

Thrown by json.NET 由json.NET抛出

Unable to find a constructor to use for type MimeKit.Header. 找不到用于MimeKit.Header类型的构造函数。 A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. 一个类应该具有一个默认构造函数,一个带有参数的构造函数或一个标有JsonConstructor属性的构造函数。 Path 'Headers[0].Offset', line 1, position 22. 路径“ Headers [0] .Offset”,第1行,位置22。

I am using the StackExchange.Redis method stringGet to get the serialised object and then passing the RedisValue into Json.Net; 我正在使用StackExchange.Redis方法stringGet来获取序列化的对象,然后将RedisValue传递到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. 我还检查了返回的JSON字符串的格式,它是正确的。

Thanks for your help. 谢谢你的帮助。

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. 考虑到这一点, MimeKit.Header类提供了6个构造函数,这些构造函数都不带任何参数。 Because of this, JSON.NET does not know how to instantiate this class, which is why the exception occurs. 因此,JSON.NET不知道如何实例化此类,这就是发生异常的原因。

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: 由于您对MimeKit.Header类没有任何控制权, 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; } }
}

Here, we're deserializing the Header class into a JObject first. 在这里,我们首先将Header类反序列化为JObject Then, we're picking the HeaderId and Value out of that JObject in order to create a Header instance. 然后,我们从该JObject中选择HeaderIdValue以便创建Header实例。

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. 这可能不是实例化Header的最佳方法,而且我可能会不小心丢弃一些信息。 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. 一旦解决了这个问题,就会遇到另一种与不接受null值的属性设置器有关的问题。

Specifically, the ResentMessageId and MimeVersion properties have logic in the setters that throw ArgumentException s if you supply null . 具体来说,如果您提供null ,则ResentMessageIdMimeVersion属性在设置器中具有引发ArgumentException的逻辑。 This is an issue, since those values can be null when the MimeMessage is instantiated. 这是一个问题,因为在实例化MimeMessage时,这些值可以为null。

One workaround for this would be to create a ContractResolver that excludes those properties: 一种解决方法是创建一个不包含那些属性的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;
    }
}

This way, we're never going to accidentally pass null to the setter for one of these properties. 这样,我们绝不会意外地将null传递给这些属性之一的设置器。

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. 这里也要注意 :我注意到,即使当我忽略MimeVersionResentMessageId ,如果标头包含MimeVersion和/或ResentMessageId标头,它们仍然会被设置。 I'm guessing this is baked into the MimeMessage somewhere, but again, I'm not totally familiar with this library. 我猜想它已经被MimeMessageMimeMessage某个地方,但是同样,我对这个库并不完全熟悉。

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. 希望这至少可以帮助您入门。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM